←back to thread

292 points kristoff_it | 10 comments | | HN request time: 1.368s | source | bottom
Show context
Retr0id ◴[] No.44609223[source]
I don't get it - the "problem" with the client/server example in particular (which seems pivotal in the explanation). But I am also unfamiliar with zig, maybe that's a prerequisite. (I am however familiar with async, concurrency, and parallelism)
replies(2): >>44609353 #>>44609377 #
1. koakuma-chan ◴[] No.44609353[source]
Example 1

You can write to one file, wait, and then write to the second file.

Concurrency not required.

Example 2

You can NOT do Server.accept, wait, and then do Client.connect, because Server.accept would block forever.

Concurrency required.

replies(2): >>44609387 #>>44610335 #
2. tines ◴[] No.44609387[source]
Oh, I see. The article is saying that async is required. I thought it was saying that parallelism is required. The way it's written makes it seem like there's a problem with the code sample, not that the code sample is correct.
replies(1): >>44609503 #
3. Retr0id ◴[] No.44609503[source]
The article later says (about the server/client example)

> Unfortunately this code doesn’t express this requirement [of concurrency], which is why I called it a programming error

I gather that this is a quirk of the way async works in zig, because it would be correct in all the async runtimes I'm familiar with (e.g. python, js, golang).

My existing mental model is that "async" is just a syntactic tool to express concurrent programs. I think I'll have to learn more about how async works in zig.

replies(1): >>44609598 #
4. sunshowers ◴[] No.44609598{3}[source]
I think a key distinction is that in many application-level languages, each thing you await exists autonomously and keeps doing things in the background whether you await it or not. In system-level languages like Rust (and presumably Zig) the things you await are generally passive, and only make forward progress if the caller awaits them.

This is an artifact of wanting to write async code in environments where "threads" and "malloc" aren't meaningful concepts.

Rust does have a notion of autonomous existence: tasks.

replies(3): >>44609714 #>>44609921 #>>44610390 #
5. Retr0id ◴[] No.44609714{4}[source]
Thanks, this clears things up for me.

I suppose I conflated "asynchrony" (as defined in the article) and "async" as a syntax feature in languages I'm familiar with.

6. m11a ◴[] No.44609921{4}[source]
I think that notion is very specific to Rust's design.

Golang for example doesn't have that trait, where the user (or their runtime) must drive a future towards completion by polling.

replies(1): >>44610103 #
7. sunshowers ◴[] No.44610103{5}[source]
Right, but Go is an application-level language and doesn't target environments where threads aren't a meaningful concept. It's more an artifact of wanting to target embedded environments than something specific to Rust.
8. jayd16 ◴[] No.44610335[source]
But why is this a novel concept? The idea of starvation is well known and you don't need parallelism for it to effect you already. What does zig actually do to solve this?

Many other languages could already use async/await in a single threaded context with an extremely dumb scheduler that never switches but no one wants that.

I'm trying to understand but I need it spelled out why this is interesting.

replies(1): >>44610713 #
9. jayd16 ◴[] No.44610390{4}[source]
Thank you so much for the added context. This explains the article very well.
10. shikon7 ◴[] No.44610713[source]
The novel concept is to make it explicit where a non-concurrent scheduler is enough (async), and where it is not (async-concurrent). As a benefit, you can call async functions directly from a synchronous context, which is not possible for the usual async/await, therefore avoiding the need to have both sync and async versions of every function.

And with green threads, you can have a call chain from async to sync to async, and still allow the inner async function to yield through to the outer async function. This keeps the benefit of async system calls, even if the wrapping library only uses synchronous functions.