Live data from Hacker News

Zig's New Async I/O

kristoff.it

231–240 of 293 posts

Re: Zig's New Async I/O

#231

I'm generally a fan of Zig, but it's a little sad seeing them go all in on green threads (aka fibers, aka stackful coroutines). Rust got rid of their Runtime trait (the rough equivalent of Zig's Io) before 1.0 because it performed badly. Languages and OS's have had to learn this lesson the hard way over and over again: https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2018/p13... > While fibers may have looked like…

Oh man. I think the biggest mistake Rust has ever made is their async model. It’s been nothing short of a disaster. Zig supporting green threads and other options is spectacularly exciting. Thus far no one has “solved” async. So very exciting to see what Zig can come up with.

Re: Zig's New Async I/O

#232
post #229
post #68

Earlier quoted context omitted.

That is lovely to hear. I think the general conscious is that not a single programming language has done Async right. So people are a little sceptical. But Andrew and the team so far seems to have the do it right mentality. So I guess people should be a little more optimistic. Cant wait for 0.15 coming out soon.

What is wrong with async in JS?

You have to write JS

Re: Zig's New Async I/O

#233
post #97

Earlier quoted context omitted.

You can take that view, yes. But if you store your context in a struct (which is not the recommend “best practice” – but which you can do) it's no longer a function coloring issue. I do that in on of my libraries and I feel that it's the right call (for that library).

If the struct has a well-scoped and short-lived lifecycle, then it is actually better to put the context in the struct. Many Go libraries including the stdlib do this despite not being "best practice". An exception to the short-lived rule is to put context in your service struct and pass it as the base context when constructing the HTTP server, so that when you get a service shutdown signal, one can cancel requests g…

It's well scoped, but not short lived; it's an SQLite connection.

But the API surface is huge, with 100s of methods on the connection and derived objects, with it being unclear which might block and be worthy of asynchronous cancellation. You never know when pulling an additional column if that one might be an overflow text/blob that does additional IO.

The solution, while not amazing is a method that you use like this:

  old := conn.SetInterrupt(ctx)
  defer conn.SetInterrupt(old)
This changes the “interrupt” context for the duration of your function scope, and covers all potentially blocking calls that you might make. Also, from the name, it's quite clear that this context is used only for interruption/cancellation (interrupt is the SQLite name for this, which I try to adhere to).

Re: Zig's New Async I/O

#234

Earlier quoted context omitted.

hm maybe. i guess ive only used python in situations where it injects it into amain so i could have been confused. i thought python async was a wrapper around generators, and so the mechanism cant be instantiated in multiple places. i stand corrected.

Oh no you're right it's a pain in the ass and weird. But I think it's the way because there's no good reason to have more than one event loop. Also... maybe it started out as generator wrappers? I think I read something that said that.

> no good reason to have more than one event loop

Per thread—once you start working in multiple threads you have the choice to have one global event loop, which comes at the cost of all async code being effectively serialized as far as threads are concerned*, or one event loop per thread.

* Which can be fine if your program is mostly not async but you have that one stubborn library. Yay async virality.

Re: Zig's New Async I/O

#235

Earlier quoted context omitted.

You can call an async function from a function that is not async by passing in a global runtime (/ event loop). As a trivial example the main entry point in rust is never async. How’d you suppose you’d bootstrap the runtime that you’d eventually need. This is pretty much like every other langage.

and yet... there are libraries that were written twice, once for async and once for not.

Which should be a pretty big hint that you've misidentified the issue.

Re: Zig's New Async I/O

#236
I'm confused. The trouble with "colored" functions is that they either do processing on the stack, or unwind the stack. They claim defeat of function coloring, and describe that IO implementation can use blocking/thread pool/green threads. But... these are all blocking methods, which weren't the problem in the first place! If you keep convention to never do IO using global state, you could do that practically in any language. Stackless coroutines being left for later feels like "draw the rest of the owl" situation.

To actually have truly universal functions, I think there are two solutions:

- Make every function async, and provide extra parameter indicating to not actually unwind the stack and execute synchronously instead. Comes with performance penalty.

- Compile each function twice, picking appropiate variant at call site. Increases code size and requires some hackery with handling function pointers.

Re: Zig's New Async I/O

#237

Earlier quoted context omitted.

Their bet seems to be that they can transparently implement real async inside an IO implementation using compiler magic. But then it means if you use that IO instance with the magic then your function gets transformed into a state machine? Then this whole thing is useless for implementing cooperative scheduling async like in rust?

> But then it means if you use that IO instance with the magic then your function gets transformed into a state machine? This was essentially like the old async/await implementation in Zig already worked. The same function gets the state-machine treatment if it was called in an async context, otherwise it's compiled as a 'regular' sequential function. E.g. at runtime there may be two versions of a function, but not i…

[deleted]

Re: Zig's New Async I/O

#238
post #81

Earlier quoted context omitted.

> Isn't "coloring" just another form of static typing? Yes, and so is declaring what exceptions a function can throw (checked exceptions in Java). > Why is it bad to have functions annotated with this meta data? The functions behave in a fundamentally different way whether you give them special annotations/syntax or not. Shouldn't different things look different? It really isn't a problem. The article makes people th…

> but IMHO people who sit down for a bit and think through the issue come to the same conclusion you have - Function coloring isn't a problem in practice. I dunno man, have you seen people complain about async virality in Rust being annoying? Have you ever tried to read a backtrace from a program that does stackless coroutines (it's not fun)? Have you seen people do basically duplicate work to maintain a blocking and…

The alternative is to jump through a bunch of hoops to hide the "coloring" behind some opaque abstraction that is complicated and that will still get in the way when things go wrong.

Everyone complained when async IO was done with callbacks, so sugar was added to the callbacks, and now everyone has spent over a decade complaining about what flavor of sugar tastes best.

Y'all at Zig have a solution, I trust Zig's solution will be a good one (zig is lots of fun to use as a language) but at the end of the day, IO is slow, that needs to get hidden somehow, or not.

Everyone should have to do embedded for awhile and setup their own DMA controller operations. Having async IO offloaded to an actual hardware block is... A different type of amusing.

Re: Zig's New Async I/O

#239

I'm confused. The trouble with "colored" functions is that they either do processing on the stack, or unwind the stack. They claim defeat of function coloring, and describe that IO implementation can use blocking/thread pool/green threads. But... these are all blocking methods, which weren't the problem in the first place! If you keep convention to never do IO using global state, you could do that practically in any…

> Make every function async, and provide extra parameter indicating to not actually unwind the stack and execute synchronously instead. Comes with performance penalty.

I think ValueTask in C#/.NET can approach this use case - It avoids overhead if the method actually completes synchronously. Otherwise, you can get at the Task if needed. From a code perspective, you await it like you normally would and the compiler/runtime figures out what to do.

Re: Zig's New Async I/O

#240
post #145

As the author of a semi-famous post about how Zig has function colors [1], I decided to read up on this. I see that blocking I/O is an option: > The most basic implementation of `Io` is one that maps to blocking I/O operations. So far, so good, but blocking I/O is not async. There is a thread pool that uses blocking I/O. Still good so far, but blocking I/O is still not async. Then there's green threads: > This implem…

> it depends on reintroducing a special function calling convention This is an internal implementation detail rather than a fact which is usually exposed to the user. This is essentially just explaining that the Zig compiler needs to figure out which functions are async and lower them differently. We do have an explicit calling convention, `CallingConvention.async`. This was necessary in the old implementation of asy…

From my experience, the calling convention was, in 0.9.x, just an implementation detail, until it wasn't. I think I may still reserve judgment for when async is fully implemented. Then I'll torture it again.
Post reply on HN