←back to thread

Async/Await is finally back in Zig

(charlesfonseca.substack.com)
39 points barddoo | 6 comments | | HN request time: 0.448s | 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. api ◴[] No.45782809[source]
I think it’s a terrible complexity multiplying workaround for the fact that we can’t fix our ancient 1970s OS APIs. Threads should be incredibly cheap. I should be able to launch them by the tens of millions, kill them at will, and this should be no more costly than goroutines.

(All modern OSes in common use are 1970s vintage under the hood. All Unix is Bell Labs Unix with some modernization and veneer, and NT is VMS with POSIX bolted on later.)

Go does this by shipping a mini VM in every binary that implements M:N thread pooling fibers in user space. The fact that Go has to do this is also a workaround for OS APIs that date back to before disco was king, but at least the programmer doesn’t have to constantly wrestle with it.

Our whole field suffers greatly from the fact that we cannot alter the foundation.

BTW I use Rust async right now pretty heavily. It strikes me as about as good as you can do to realize this nightmare in a systems language that does not ship a fat runtime like Go, but having to actually see the word “async” still makes me sad.

replies(3): >>45783045 #>>45783150 #>>45783229 #
2. nananana9 ◴[] No.45783045[source]
You don't need a fat runtime to do fibers/stackful coroutines. You don't need any language support for that matter, just 50 lines of assembly to save registers on the stack and switch stack pointers. Minicoro [1] is a C library that implements fibers in a single header (just the creation/destruction/context switching, you have to bring your own scheduler).

Our game engine has a in-house implementation - creating a fiber, scheduling it, and waiting for it to complete takes ~300ns on my box. Creating a OS thread and join()ing is just about 1000 slower, ~300us.

[1] https://github.com/edubart/minicoro

replies(1): >>45783249 #
3. zozbot234 ◴[] No.45783150[source]
"Threads" are expensive because they are OS-managed "virtual" cores as seen by the current process. You can run coroutines as "user-level" tasks on top of kernel threads, and both Go and Rust essentially allow this, though in slightly different ways.
4. sapiogram ◴[] No.45783229[source]
Kill threads at will?
replies(1): >>45784242 #
5. lukaslalinsky ◴[] No.45783249[source]
I have even simpler version:

https://github.com/lalinsky/zio/blob/main/src/coroutines.zig

Which has the benefit of Zig single unit of compilation, that the compiler can be smarter about which registers need to be saved.

6. api ◴[] No.45784242[source]
That requires some explanation. Basically I think runtimes should be abort safe and have some defined thing that happens when a thread is aborted. Antiquated 70s blocking APIs do not, or do not consistently.

It’s a minor gripe compared to the heaviness of threads and making every programmer hand roll fibers by way of async.