Seven Async Runtimes, No Two Agree on a Simple Logging Program
A Design Space Exploration of Async/Await

Brown University researchers analyzed async/await across seven runtimes—Asyncio, C#, JavaScript, Tokio, Smol, Trio, and Swift—and found they produce four different outputs for a trivial fire-and-forget logging task. They identify nine design dimensions, from eagerness to cancellation persistence, and formalize them in a core calculus to explain why concurrency semantics diverge so wildly.
There isn’t really a right answer, because you were probably right for some language.
- spankalee
Wow, this is really helpful and timely!
I'm building a new language with async/await and had to make a lot of these decisions, but I didn't have this organized of a framework to ground myself in. I'm happy to see it clearly that I choose mostly Trio with a bit of JavaScript.
My language (Zena's) async docs page: https://zena-lang.dev/guide/async/ I think I might do a pass and try to call out the decision points more explicitly.
fwiw, I found this post on cancellation by the author of Trio to be vey compelling: https://vorpus.org/blog/timeouts-and-cancellation-for-humans... and I based the cancellation design of Zena on it.
Edit to add: I do wish this included JavaScript's AbortSignal in the Cancellation section. Not because it's good, but because passing cancel tokens is a pattern that exists. There's also the dimension of who can cancel and, like AbortSignal, whether tasks have to opt-in to cancellation checks.
- hankbond
> You must be a JavaScript developer.
and i took that personally
- biorach
It's long been clear that there were fundamental implementation choices that mattered between async runtimes, but _nine_ design dimensions? Damn.
I think async is deceptive in that it seems like a self-contained and relatively straightforward aspect of a language. But there are many design choices to be made and they all have wide implications.
Plus I think the implications of many of these dimensions are not fully understood and that collectively we are still trying to understand how they are playing out in implementations. Add to this the subtle nature of some of the implications plus the combinations...
I think a good comparison is lexical vs dynamic scope in programming languages. This is a design dimension that was argued over for a decade or two in the early years of programming language design. It was only as time went by, and experience gained by working with concrete implementations that it became clear that lexical scoping should be the default choice and dynamic scoping should be restricted to various niches.
- jcelerier
I was wondering "hopefully C++ allows you to pick across these axes so that you can build yourself the async primitives that work best for the problem at hand" and then: yes!
> We cannot attribute C++ to any particular design point in the taxonomy provided in Table 1 because each axis is configurable. Although elegant and neutral, the choice of full programmability makes each library an async dsl; knowledge transfer between projects within the same language becomes exceedingly difficult.
It is not if you think in terms of these axes and which solve your particular problem and not any particular specific design. Take for instance the simplest program one can imagine: a network video player. E.g. some server sends you RTP audio & video frames and you have to play them back correctly, with a nice GUI on top.
If you want to do this in a way that is as efficient as possible you need to be aware of all possible ways of async interoperation:
- connecting & receiving packets from the network in a classic network state machine where coroutines shine
- handling vsync vs not-vsync for displaying the video frame
- conforming to whatever async paradigm the hardware video decoding system you want to use is going to provide you with, e.g. Intel QuickSync vs VideoToolbox vs NVDEC...
- handling the synchronous model of audio playback driven in pull mode
- handling the synchronisation between audio / video, and thus the async patterns that support multi-threading as your audio thread can't be […]
- biorach
At last someone took the time to pore over all the tedious crap that I have been trying and failing to keep straight in my head since forever.