←back to thread

292 points kristoff_it | 3 comments | | HN request time: 0.998s | source
1. nemothekid ◴[] No.44611569[source]
I think I'm missing something here, but the most interesting piece here is how would stackless coroutines work in Zig?

Since any function can be turned into a coroutine, is the red/blue problem being moved into the compiler? If I call:

     io.async(saveFileA, .{io});
Is that a function call? Or is that some "struct" that gets allocated on the stack and passed into an event loop?

Furthermore, I guess if you are dealing with pure zig, then its fine, but if you use any FFI, you can potentially end up issuing a blocking syscall anyways.

replies(1): >>44611729 #
2. laserbeam ◴[] No.44611729[source]
I hope this is not a bad answer as I tried to understand what stackless coroutines even are for the past week.

1. Zig plans to annotate the maximum possible stack size of a function call https://github.com/ziglang/zig/issues/23367 . As people say, this would give the compiler enough information to implemented stackless coroutines. I do not understand well enough why that’s the case.

2. Allegedly, this is only possible because zig uses a single compilation unit. You are very rarely dealing with modules that are compiled independently. If a function in zig is not called, it’s not compiled. I can see how this helps with point 1.

3. Across FFI boundaries this is a problem in every language. In theory you can always do dumb things after calling into a shared library. A random C lib can always spawn threads and do things the caller isn’t expecting. You need unsafe blocks in rust for the same reason.

4. In theory, zig controls the C std library when compiling C code. In some cases, if there’s only one Io implementation used for example, zig could replace functions in the c std library to use that io vtable instead.

Regardless, I kinda wish kristoff/andrew went over what stackless coroutines are (for dummies) in an article at some point. I am unsure people are talking about the same thing when mentioning that term. I am happy to wait for that article until zig tries to implement that using the new async model.

replies(1): >>44615988 #
3. kibwen ◴[] No.44615988[source]
> As people say, this would give the compiler enough information to implemented stackless coroutines.

Stackless coroutines require creating a structure big enough to hold all the locals for the would-be function, but Zig already has that information, along with Rust, and, by definition, every other language that already supports stackless coroutines.

The root of that linked issue is that Zig has some desire to statically compute the total stack usage of an entire program, but the difficulty there is not in computing the stack size for any given function (which is generally trivial in most languages; supporting growable stack via something like C's `alloca` is the exception, not the rule). The difficulty is that recursive functions can push an unbounded number of function calls to the stack. So what Zig wants to do is forbid recursion, even mutual recursion, unless you do some kind of opt-in.

And this is where "Allegedly, this is only possible because zig uses a single compilation unit" comes in, because detecting mutual recursion is tricky, especially when virtual dispatch gets involved.

But no, you don't need Zig-style whole-program compilation to make that happen. All you need is 1) to be able to detect mutual recursion within a single compilation unit (again, stymied by virtual dispatch), and then 2) to prevent cyclical dependencies between compilation units. Go and Rust both do the latter, so they could get away with the same analysis, assuming you can find a good solution for the former.