←back to thread

Async/Await is finally back in Zig

(charlesfonseca.substack.com)
39 points barddoo | 10 comments | | HN request time: 1.145s | source | bottom
Show context
dilawar ◴[] No.45782574[source]
Someone has historical insights into why async/await seems to have taken over the world?

I often write Rust and I don't find it very attractive, but so many good projects seem to advertise it as a "killer feature". Diesel.rs doesn't have async, and they claim that perf improvement may not be worth it (https://users.rust-lang.org/t/why-use-diesel-when-its-not-as...).

For a single threaded JS program, async makes a lot of sense. I can't imagine any alternative pattern to get concurrency so cleanly.

replies(8): >>45782621 #>>45782683 #>>45782771 #>>45782778 #>>45782809 #>>45782885 #>>45782984 #>>45784113 #
1. lukaslalinsky ◴[] No.45782621[source]
https://en.wikipedia.org/wiki/C10k_problem

Because when you require 1 thread per 1 connection, you have trouble getting to thousands of active connections and people want to scale way beyond that. System threads have overhead that makes them impractical for this use case. The alternatives are callbacks, which everybody hates and for a good reason. Then you have callbacks wrapped by Futures/Promises. And then you have some form of coroutines.

Keeping in mind that what Zig is introducing is not what languages call async/await. It's more like the I/O abstraction inside Java, where you can use the same APIs with platform threads and virtual threads, but in Zig, you will need to pass the io parameter around, in Java, it's done in the background.

replies(2): >>45782674 #>>45782709 #
2. troupo ◴[] No.45782674[source]
> The alternatives are callbacks

No. The alternative is lightweight/green threads and actors.

The thing with await is that it can be retrofitted onto existing languages and runtimes with relatively little effort. That is, it's significantly less effort than retrofitting an actual honest-to-god proper actor system a la Erlang.

replies(3): >>45782802 #>>45782812 #>>45783167 #
3. matheusmoreira ◴[] No.45782709[source]
> The alternatives are callbacks, which everybody hates and for a good reason. Then you have callbacks wrapped by Futures/Promises. And then you have some form of coroutines.

The event loop model is arguably equivalent to coroutines. Just replace yield with return and have the underlying runtime decide which functions to call next by looping through them in a list. You can even stall the event loop and increase latency if you take too long to return. It's cooperative multitasking by another name.

replies(2): >>45783101 #>>45783204 #
4. antihero ◴[] No.45782802[source]
Isn’t await often just sugar around the underlying implementation be that greenthreads, epoll, picoev, etc?
replies(1): >>45782993 #
5. matheusmoreira ◴[] No.45782812[source]
> The alternative is lightweight/green threads and actors.

How lightweight should threads be to support high scale multitasking?

Writing my own language, capturing stack frames in continuations resulted in figures like 200-500 bytes. Grows with deeply nested code, of course, but surely this could be optimized...

https://www.erlang.org/docs/21/efficiency_guide/processes.ht...

This document says Erlang processes use 309 words which is in the same ballpark.

replies(1): >>45782962 #
6. troupo ◴[] No.45782962{3}[source]
I didn't have to answer :) Thank you for looking it up.

Erlang also enjoys quite a lot of optimizations on the VM level. E.g. a task is parked/hybernated if there's no work for it to perform (e.g. it's waiting for a message), the switch between tasks is extremely lightweight, VM internals are re-entrant, employ CPU-cache-friendly data structures, garbage collection is both lightweight and per-thread/task etc.

7. troupo ◴[] No.45782993{3}[source]
I think it depends on the language?

Javascript's async/await probably started as a sugar for callbacks (since JS is single-threaded). Many others definitely have that as sugar for whatever threading implementation they have. In C# it's sugar on top of the whole mechanism of structured concurrency.

But I'm mostly talking out of my ass here, since I don't know much about this topic, so everything above is hardly a step above speculation.

8. zozbot234 ◴[] No.45783101[source]
Coroutines/resumable functions are not restricted to yielding to a single runtime or event loop, they can simply "resume" each other directly. There are also extensions of coroutines that are more than one-shot (a resumable function where the current state can be copied and invoked more than once) and/or are allowed to provide values when "resuming" other code, which also goes beyond the common "event loop" model.
9. lukaslalinsky ◴[] No.45783167[source]
> The alternative is lightweight/green threads and actors.

Those are all some form of coroutines.

10. lukaslalinsky ◴[] No.45783204[source]
It's all the same concept, it's just a matter who/what is managing the state while you are waiting for I/O. When you yield, it's the compiler/runtime making sure the context is saved. When you return, it's your responsibility.