I like the look of this direction. I am not a fan of the `async` keyword that has become so popular in some languages that then pollutes the codebase.
Zig's new plan for asynchronous programs
61–70 of 274 posts
Re: Zig's new plan for asynchronous programs
#62Re: Zig's new plan for asynchronous programs
#63Eg.
doSomethingAsync().defer
This removes stupid parentheses because of precedence rules.
Biggest issue with async/await in other languages.
Re: Zig's new plan for asynchronous programs
#64One thing the old Zig async/await system theoretically allowed me to do, which I'm not certain how to accomplish with this new io system without manually implementing it myself, is suspend/resume. Where you could suspend the frame of a function and resume it later. I've held off on taking a stab at OS dev in Zig because I was really, really hoping I could take advantage of that neat feature: configure a device or sub…
Re: Zig's new plan for asynchronous programs
#65I find this example quite interesting: var a_future = io.async(saveFile, .{io, data, "saveA.txt"}); var b_future = io.async(saveFile, .{io, data, "saveB.txt"}); const a_result = a_future.await(io); const b_result = b_future.await(io); In Rust or Python, if you make a coroutine (by calling an async function, for example), then that coroutine will not generally be guaranteed to make progress unless someone is waiting f…
C# works like this as well, no? In fact C# can (will?) run the async function on the calling thread until a yield is hit.
Re: Zig's new plan for asynchronous programs
#66Love it, async code is a major pita in most languages.
When Microsoft added Tasks / Async Await, that was when I finally stopped writing single threaded code as often as I did, since the mental overhead drastically went away. Python 3 as well.
Every other example I've seen encodes the execution model in the source code.
Re: Zig's new plan for asynchronous programs
#67I find this example quite interesting: var a_future = io.async(saveFile, .{io, data, "saveA.txt"}); var b_future = io.async(saveFile, .{io, data, "saveB.txt"}); const a_result = a_future.await(io); const b_result = b_future.await(io); In Rust or Python, if you make a coroutine (by calling an async function, for example), then that coroutine will not generally be guaranteed to make progress unless someone is waiting f…
It's not guaranteed in Zig either. Neither task future is guaranteed to do anything until .await(io) is called on it. Whether it starts immediately (possibly on the same thread), or queued on a thread pool, or yields to an event loop, is entirely dependent on the Io runtime the user chooses.
> When using an Io.Threaded instance, the async() function doesn't actually do anything asynchronously — it just runs the provided function right away. So, with that version of the interface, the function first saves file A and then file B. With an Io.Evented instance, the operations are actually asynchronous, and the program can save both files at once.
Andrew Kelley’s blog (https://andrewkelley.me/post/zig-new-async-io-text-version.h...) discusses io.concurrent, which forces actual concurrency, and it’s distinctly non-structured. It even seems to require the caller to make sure that they don’t mess up and keep a task alive longer than whatever objects the task might reference:
var producer_task = try io.concurrent(producer, .{
io, &queue, "never gonna give you up",
});
defer producer_task.cancel(io) catch {};
Having personally contemplated this design space a little bit, I think I like Zig’s approach a bit more than I like the corresponding ideas in C and C++, as Zig at least has defer and tries to be somewhat helpful in avoiding the really obvious screwups. But I think I prefer Rust’s approach or an actual GC/ref-counting system (Python, Go, JS, etc) even more: outside of toy examples, it’s fairly common for asynchronous operations to conceptually outlast single function calls, and it’s really really easy to fail to accurately analyze the lifetime of some object, and having the language prevent code from accessing something beyond its lifetime is very, very nice. Both the Rust approach of statically verifying the lifetime and the GC approach of automatically extending the lifetime mostly solve the problem.But this stuff is brand new in Zig, and I’ve never written Zig code at all, and maybe it will actually work very well.
Re: Zig's new plan for asynchronous programs
#68Earlier quoted context omitted.
Async usually ends up being a coloring function that knows no bounds once it is used.
I’ve never really understood the issue with this. I find it quite useful to know what functions may do something async vs which ones are guaranteed to run without stopping. In my current job, I mostly write (non-async) python, and I find it to be a performance footgun that you cannot trivially tell when a method call will trigger I/O, which makes it incredibly easy for our devs to end up with N+1-style queries withou…
Re: Zig's new plan for asynchronous programs
#69Overall this article is accurate and well-researched. Thanks to Daroc Alden for due diligence. Here are a couple of minor corrections: > When using an Io.Threaded instance, the async() function doesn't actually do anything asynchronously — it just runs the provided function right away. While this is a legal implementation strategy, this is not what std.Io.Threaded does. By default, it will use a configurably sized th…
Re: Zig's new plan for asynchronous programs
#70Earlier quoted context omitted.
It's not guaranteed in Zig either. Neither task future is guaranteed to do anything until .await(io) is called on it. Whether it starts immediately (possibly on the same thread), or queued on a thread pool, or yields to an event loop, is entirely dependent on the Io runtime the user chooses.
It’s not guaranteed, but, according to the article, that’s how it works in the Evented model: > When using an Io.Threaded instance, the async() function doesn't actually do anything asynchronously — it just runs the provided function right away. So, with that version of the interface, the function first saves file A and then file B. With an Io.Evented instance, the operations are actually asynchronous, and the progra…