# What is the status of async with Zig?

**URL:** <https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715>\
**Category:** Explain\
**Tags:** language\
**Created:** [August 22, 2024, 8:51am UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715 "2024-08-22T08:51:15Z")\
**Posts on this page:** 20\
**Page:** 3

<div class="post-metadata">

**Author:** ![GigaGrunch](https://ziggit.dev/user_avatar/ziggit.dev/gigagrunch/32/1823_2.png) [@GigaGrunch](https://ziggit.dev/u/GigaGrunch)\
**Post date:** [September 2, 2024, 9:10am UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/41 "2024-09-02T09:10:58Z")

</div>

Sorry, off topic:

> the [function coloring problem](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/)

I didn’t know this one yet. Nice one 😁 Made me think more about constness though. Especially in C++ 😅

---

<div class="post-metadata">

**Author:** ![buzmeg](https://ziggit.dev/user_avatar/ziggit.dev/buzmeg/32/622_2.png) [@buzmeg](https://ziggit.dev/u/buzmeg)\
**Post date:** [September 3, 2024, 12:17am UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/42 "2024-09-03T00:17:15Z")

</div>

> [@dee0xeed](#):
>
> but RAII (C++ way to handle “resources”)  
> was always a thing that confused me a lot.

Implement something with reference counting and suddenly you will understand what RAII is and why it’s so useful. Take a look at the Linux kernel and how much it uses reference counting and the bugs. Doing reference counted stuff in Zig or C is _PAIN_.

In my opinion, RAII is _THE_ dividing line between small systems programming languages and big ones. If you omit RAII, you have C and Zig. If keep RAII, you have C++ and Rust.

A larger question is whether RAII is even a good idea nowadays. RAII results in things scattered across the heap and lots of fragmentation. This is death to performance on modern CPUs. If you let your RAII become non-deterministic, you’re pretty much back to a garbage collector.

Perhaps someone very clever in the Zig community will figure out how to do reference counted stuff better without RAII. We’ll have to see.

(Personally, I’d rather see efforts to support state machines more directly in some way. State machines are _way_ more important in my opinion than RAII.)

---

<div class="post-metadata">

**Author:** ![dee0xeed](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/3ab097/32.png) [@dee0xeed](https://ziggit.dev/u/dee0xeed)\
**Post date:** [September 3, 2024, 7:31am UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/43 "2024-09-03T07:31:50Z")

</div>

> [@buzmeg](#):
>
> Implement something with reference counting and suddenly you will understand what RAII is and why it’s so useful

I do know what RAII is. Maybe I used wrong words. I meant that I do not like / feel uncomfortable when a program is doing something _implicitly_ (frees memory, closes files etc at the end of a scope in case of RAII). Well, consider doing everything explicitly as my (or someone’s else) personal preference.

> [@buzmeg](#):
>
> Take a look at the Linux kernel and how much it uses reference counting and the bugs

I only remember about file open count (I had some experience in writing device drivers quite a long time ago) - driver `release` method is invoked only at last `close` call, something like that. I am not sure if memory allocation/de-allocation (`kmalloc/kfree`) uses some ARC stuff.

> [@buzmeg](#):
>
> n my opinion, RAII is _THE_ dividing line between small systems programming languages and big ones. If you omit RAII, you have C and Zig. If keep RAII, you have C++ and Rust.

👍  
Wow. I really like this definition!

---

<div class="post-metadata">

**Author:** ![huntrss](https://ziggit.dev/user_avatar/ziggit.dev/huntrss/32/6342_2.png) [@huntrss](https://ziggit.dev/u/huntrss)\
**Post date:** [September 3, 2024, 10:54am UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/44 "2024-09-03T10:54:16Z")

</div>

> [@buzmeg](#):
>
> personally, I’d rather see efforts to support state machines more directly in some way. State machines are _way_ more important in my opinion than RAII.

Don’t you think that tagged unions or using something like the typestate pattern as used in Rust ([How To Use The Typestate Pattern In Rust | Zero To Mastery](https://zerotomastery.io/blog/rust-typestate-patterns/)) is sufficient?

---

<div class="post-metadata">

**Author:** ![Sze](https://ziggit.dev/user_avatar/ziggit.dev/sze/32/496_2.png) [@Sze](https://ziggit.dev/u/Sze)\
**Post date:** [September 3, 2024, 11:17am UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/45 "2024-09-03T11:17:09Z")

</div>

I think RAII has a similar problem like Java, that it causes too many verbs (functions) to become nouns (classes).

Instead of having a database handle and then just calling `const conn = db.connect(...)` on it and `defer db.disconnect(conn)`. It now wants you to create a Connection object that can do essentially the same thing but bind these things to a scope where the end of scope causes the disconnect.

This means that if you have something that has many sequential steps you now need to create many of those nested scope things to follow that philosophy, I find that annoying, I think these ideas work and are “beneficial” only until you get tired of doing unnecessary work of transforming your straightforward program into one that is approved by RAII ideology.

In the end I think it is an ideology and one I don’t find particular appealing, because it overemphasizes a false sense of programmer “safety” and “correctness”, over writing code that actually results in good memory layout, not wasting cache lines etc.

It hides details, forces your program into an arbitrary structure with questionable benefits and thus makes it more difficult to optimize things that actually matter, later. It forces a way of thinking and structuring the code, so that people can avoid thinking about the things they should actually think about. Things like:

- How much memory does this take?
- How much instances I have of this thing?
- Why are they scattered all around all over the place, instead of collected in a single array?
- Why are there many objects of different types collected in lists instead of having multiple arrays of a single type?

Sometimes it’s really best to just have things mixed in a list, but I think that with the RAII way of doing things it is more often the default outcome, instead of a deliberate choice.

I think we should optimize for doing things in a way that brings us to consider and pick a lot of meaningful deliberate choices until we are done with the program.

With RAII I find it difficult to see what the code is actually doing, more and more abstraction is piled up until it is hard to tell what is going on, I think non-RAII languages have a tendency to keep it simpler and less abstracted, putting more responsibility on the programmer, but also not creating a false/fake sense of security (in the situations where things where made unnecessarily complicated, just so that the code philosophy can be followed).

I also think that this wrapping things is a distraction that makes people think about “code architecture” instead of just writing the code and then seeing from that, the pieces that are worth abstracting out / seeing what repeats and can be formalized.

In the end it is probably a subjective choice.  
But I would rather have the responsibility of avoiding to shoot/stab myself in the foot, then having to wrap every tool in a foot-shoot/stab-prevention wrapper all the time.

Data oriented ideas seem much more practical to me, because they actually care about the hardware the program later runs on, than abstract claims of RAII being useful (without considering where it isn’t and what it makes more annoying).

---

<div class="post-metadata">

**Author:** ![tgirod](https://ziggit.dev/user_avatar/ziggit.dev/tgirod/32/3744_2.png) [@tgirod](https://ziggit.dev/u/tgirod)\
**Post date:** [September 3, 2024, 2:54pm UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/46 "2024-09-03T14:54:21Z")

</div>

> [@buzmeg](#):
>
> (Personally, I’d rather see efforts to support state machines more directly in some way. State machines are _way_ more important in my opinion than RAII.)

Do you mind expanding a bit about FSM and concurrent execution ? I’m familiar with both but not with how they relate.

---

<div class="post-metadata">

**Author:** ![dee0xeed](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/3ab097/32.png) [@dee0xeed](https://ziggit.dev/u/dee0xeed)\
**Post date:** [September 3, 2024, 3:25pm UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/47 "2024-09-03T15:25:01Z")

</div>

> [@tgirod](#):
>
> Do you mind expanding a bit about FSM and concurrent execution ? I’m familiar with both but not with how they relate

Take a look at [this](https://github.com/dee0xeed/edsm-in-zig-demo-2). This is an alternative to async/await (coroutines) that does not require special support on compiler side, as well as coding in asm.

---

<div class="post-metadata">

**Author:** ![const-void](https://ziggit.dev/user_avatar/ziggit.dev/const-void/32/63_2.png) [@const-void](https://ziggit.dev/u/const-void)\
**Post date:** [September 4, 2024, 9:46am UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/48 "2024-09-04T09:46:55Z")

</div>

Admiral Javascript, don’t be too proud of this technological terror you’ve constructed. **The ability to `async` is insignificant next to the power of `fork` w/IPC. 👿**

re: evolution

1. We are fundamentally, very, very, very lazy. To wit, we haven’t elevated beyond the interface laid out by teletypewriters (1902).

2. CPU and RAM are there to be used. A well-designed system should drive to 100% CPU and 100% RAM consumption because idle resources are wasted (non-deterministic events are a tax that suppresses resource utilization).

3. Turing taught us we can do anything with anything, but #1 says to wear the right underwear.

- For graceful, non-blocking mvvm, Swift.
- For “just do it for the masses”, python.
- For code that must last _another_ 40 years, C (for now).
- For those nights of shame, c++

Enter zig. async in zig appeals to my sense of laziness. A concept I don’t have to know but can easily use, because hey, `pthread` was a thing and `async` is way easier to type.

However, is that a good thing? That I don’t know nor care to learn? No. In architectural terms, async is akin to the brutalist style of large concrete buildings, one size fits all concrete for the masses. _async is inherently problematic._

We, and thus the world, are better off with elegant, bespoke designs and patterns that solve a specific problem - a Sistine Chapel solution for the problem…a library that does precisely what is needed, with a thread / signal / IO paradigm designed _around that problem_. to me, this is the zig use-case.

Great thread!

---

<div class="post-metadata">

**Author:** ![dee0xeed](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/3ab097/32.png) [@dee0xeed](https://ziggit.dev/u/dee0xeed)\
**Post date:** [September 5, 2024, 9:41am UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/49 "2024-09-05T09:41:37Z")

</div>

> [@const-void](#):
>
> async in zig appeals to my sense of laziness

Funny, I’v got more or less similar thoughts/feeling about RAII. Externally it looks just like this - a lot of hard mental work done by a language/compiler designers, but for what? Just to let lazy/capricious/beginner/forgetful programmers omit cleanup code? Is it really THAAAT hard to write cleanup code explicitly? In C I’m quite happy with [`goto __cleanup`](https://ziggit.dev/t/c-goto-vs-zig-defer-errdefer-break/2952/4) way, In Zig we have `defer/errdefer`. The latter a bit harder to grasp imo, but we have some [sensible rules](https://ziggit.dev/t/managing-file-reading-closing-buffer-allocation-and-deallocation/2389/9).

---

<div class="post-metadata">

**Author:** ![alcuin](https://ziggit.dev/user_avatar/ziggit.dev/alcuin/32/3264_2.png) [@alcuin](https://ziggit.dev/u/alcuin)\
**Post date:** [September 15, 2024, 8:25am UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/50 "2024-09-15T08:25:08Z")

</div>

> Continuations are the functional expression of the GOTO

This is not really correct. A (classical, or undelimited) continuation represents “the rest of the program”. [This may be a good link, scroll towards the bottom, if you are not familiar with it](http://community.schemewiki.org/?call-with-current-continuation-for-C-programmers). The problems with memory consumption in undelimited continuations are an obvious issue. See Oleg Kiselyov’s page on why call/cc (with undelimited continuations) is bad [for a variety of reasons](https://okmij.org/ftp/continuations/against-callcc.html), including those which may be of a similar mind to Zig.

The reason for (delimited) continuations is that they give very explicit and granular expression of control flow. This would align with Zig’s “make intent explicit” and otherwise “no hidden control flow”. They also have favorable memory characteristics although I haven’t dug super deep into the literature on say, affine or linear typing for continuation passing in this way (that way you could also work towards “no hidden memory allocations”).

---

<div class="post-metadata">

**Author:** ![dee0xeed](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/3ab097/32.png) [@dee0xeed](https://ziggit.dev/u/dee0xeed)\
**Post date:** [September 15, 2024, 9:32am UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/51 "2024-09-15T09:32:32Z")

</div>

> [@alcuin](#):
>
> [This may be a good link](http://community.schemewiki.org/?call-with-current-continuation-for-C-programmers)

Yes, very joyful text, thanks.

> Here’s the secret: it’s `setjmp`/`longjmp`

I used these risky guys only once and it was more than a decade ago.  
Specifically the scenario was as follows.  
Suppose you are using some DLL. And you are afraid that DLL may segfault, but you do not want to terminate just because it’s not the fault of the main program, it’s bad DLL.  
Ok, do the following.

- set a flag, say, `bad_dll` to `false`
- set a handler for SIGSEGV
- prepare jump
- check the flag, if it is true, say bad words about DLL
- otherwise attempt to call a function from DLL
- restore original SIGSEGV handler

In the SIGSEGV handler:

- set `bad_dll` to `true`
- `longjmp`

What I want to say… all that kinda clever and cool, but it is also a very nice way to confuse a reader of source code since flow control with such tricks is a bit weird imho.

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [September 15, 2024, 2:28pm UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/52 "2024-09-15T14:28:24Z")

</div>

Welcome to Ziggit @alcuin!

> [@alcuin](#):
>
> The reason for (delimited) continuations is that they give very explicit and granular expression of control flow. This would align with Zig’s “make intent explicit” and otherwise “no hidden control flow”. They also have favorable memory characteristics although I haven’t dug super deep into the literature on say, affine or linear typing for continuation passing in this way (that way you could also work towards “no hidden memory allocations”).

Although I’m a delimited continuation respecter, there are marked and unsolved problems with introducing them as a control-flow primitive in Zig. Canonically, they’re stackful (capture a series of stack frames, not just one) and resumable, and that introduces a much harder version of the [cancelawait problem](https://github.com/ziglang/zig/issues/5913) which is the #1 reason Zig `async` hasn’t returned.

Zig is low level enough that it would be possible to write a library for delimited continuations, with some amount of assembler (clearly this takes the rare skill of being a polyglot assembly expert, but it isn’t _that_ different from coroutines, which could form a basis). The big downside there is that assembly blocks are ‘optimization blind’, but as a way of exploring how those problems could be solved, and also just to have them, it’s tractable.

@mlugg (yay!) posted [on Reddit](https://old.reddit.com/r/Zig/comments/1d66gtp/state_of_async_in_zig/) (booo!) about other factors in reincorporating async, and everything listed there is as severe or more so for delimited continuations.

Last but not least, I don’t think ‘colorless’ delimited continuations are possible, and, while Zig’s OG `async` wasn’t _truly_ colorless, it got pretty close, and that was one of the best things about it. Delimited continuations are even more exotic than coroutines, so adding them as a core primitive would create an entire dialect of the language which users can’t ignore (function coloring problem) and won’t recognize.

Not to be a downer about it. It would be worthwhile to see how many of these problems could be solved, because the technique is an elegant one for certain problems of interest.

---

<div class="post-metadata">

**Author:** ![dee0xeed](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/3ab097/32.png) [@dee0xeed](https://ziggit.dev/u/dee0xeed)\
**Post date:** [September 16, 2024, 10:56am UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/53 "2024-09-16T10:56:19Z")

</div>

> [@mnemnion](#):
>
> specifically a ‘stackless’ coroutine

Sorry for backward question. 😑

Do I understand correctly that [this](https://fanf.livejournal.com/105413.html) (`setjmp()/longjmp()` based) implementation are “stackless coroutines” (they don’t have individual stacks, they just reserve some memory in common process stack) and [this one](https://dev.to/visheshpatel/implementation-of-coroutine-in-c-language-3fb2) (`{get/set/make/swap}context()` based) are “stackful coroutines” (each one allocates space for it’s stack on the heap)?

---

<div class="post-metadata">

**Author:** ![dimdin](https://ziggit.dev/user_avatar/ziggit.dev/dimdin/32/1457_2.png) [@dimdin](https://ziggit.dev/u/dimdin)\
**Post date:** [September 16, 2024, 11:36am UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/54 "2024-09-16T11:36:24Z")

</div>

Both set/longjmp and get/setcontext save the cpu registers.

- `setjmp` saves a smaller set, the exact set differs for each cpu. The registers are usually: instruction pointer, stack pointer, the stack frame pointer and the register(s) that stores the C return values.
- `getcontext` saves the entire cpu state, all the registers plus vectors and floating point registers.

**None of them copies the stack.**

Since both support getting and setting the stack pointer they can be used to change or restore stack contents.  
Also note that sigaction handler receives a pointer to the context of the CPU (the same context structure that getcontext fills) that is captured when the signal interrupts the cpu. You can change these values and run something else after the signal handler returns.

---

<div class="post-metadata">

**Author:** ![dee0xeed](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/3ab097/32.png) [@dee0xeed](https://ziggit.dev/u/dee0xeed)\
**Post date:** [September 16, 2024, 2:32pm UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/55 "2024-09-16T14:32:34Z")

</div>

> [@dimdin](#):
>
> None of them copies the stack.

wait… I am not talking about `{set,long}jmp` vs `{get/set}context`. I am talking about those two specific implementations regardless of what magic they use.

In the first one coroutines do not have “personal” stacks.  
And `longjmp` does not switch stacks just because there is only one.  
Hence I thought this is an implementation of “stackless coroutines”.

In the second one there are many stacks, one per coroutine,  
they are allocated via `ctx.uc_stack.ss_sp = calloc(1, MINSIGSTKSZ);`  
And `swapcontext` do switch stacks.

So what kind of coroutines do these examples implement?  
Both “stackless”? Or the first one is “stackless”, and the second one is “stackful”?

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [September 16, 2024, 3:22pm UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/56 "2024-09-16T15:22:46Z")

</div>

These are both stackful coroutine implementations. They allocate a certain amount of space for a program stack, jump execution to it, and from there you have an ordinary down-growing program stack, with as much room as was allocated.

Any time you want to field from that stack back to the main stack (really the calling stack), you can, they have slightly different ways of holding onto the stack context but not that different.

A stackless coroutine gives you one stack frame, corresponding to the body of one function call:

```zig
fn oneStack(...) void {
    // a stackless coro can yield anywhere in here
    // ...
    // but not in here
    _ = pushNextStack(...);
}

```

A stackless coroutine can _call_ as many functions as it would like, but it can’t _yield_ in those functions. Only in its own function body.

---

<div class="post-metadata">

**Author:** ![dimdin](https://ziggit.dev/user_avatar/ziggit.dev/dimdin/32/1457_2.png) [@dimdin](https://ziggit.dev/u/dimdin)\
**Post date:** [September 16, 2024, 3:26pm UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/57 "2024-09-16T15:26:36Z")

</div>

> [@dee0xeed](#):
>
> they are allocated via `ctx.uc_stack.ss_sp = calloc(1, MINSIGSTKSZ);`  
> And `swapcontext` do switch stacks.

Actually `uc_stack` is a `stack_t` that holds where is the stack and its size.  
The stack pointer is part of the `uc_mcontext` `mcontext_t` type.

> [@dee0xeed](#):
>
> So what kind of coroutines do these examples implement?

Since it is up to you to manipulate the stack and the instruction pointer you can have any kind of continuations and any kind of coroutines.

---

<div class="post-metadata">

**Author:** ![dee0xeed](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/3ab097/32.png) [@dee0xeed](https://ziggit.dev/u/dee0xeed)\
**Post date:** [September 16, 2024, 4:07pm UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/58 "2024-09-16T16:07:01Z")

</div>

> [@mnemnion](#):
>
> These are both stackful coroutine implementations

Aha! It seems I’ve already got it.  
That _local_ array in `cogo`,

```zig
char n[STACKDIR (tos - (char*)&arg)];

```

in fact is the personal stack for a coroutine instance, right?  
So the difference between those two implementations  
is where they hold per coroutine stacks,  
in the first one they are “chunks” of common process/thread stack  
and in the second one they are on the heap.

---

<div class="post-metadata">

**Author:** ![dee0xeed](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/3ab097/32.png) [@dee0xeed](https://ziggit.dev/u/dee0xeed)\
**Post date:** [September 16, 2024, 4:31pm UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/59 "2024-09-16T16:31:45Z")

</div>

> [@mnemnion](#):
>
> A stackless coroutine can _call_ as many functions as it would like, but it can’t _yield_ in those functions. Only in its own function body

Yes, functions polychromatism hell, I remember 🙂

---

<div class="post-metadata">

**Author:** ![ityonemo](https://ziggit.dev/user_avatar/ziggit.dev/ityonemo/32/4980_2.png) [@ityonemo](https://ziggit.dev/u/ityonemo)\
**Post date:** [March 29, 2025, 5:26pm UTC](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715/60 "2025-03-29T17:26:13Z")

</div>

not to necro this, but if anyone in the future is looking at this: language async support does _not_ imply creating a runtime, although zig will instrument your program with a runtime from std if you implement `pub fn main` as async (zig also creates a small runtime if for example `pub fn main` returns a error union, or void instead of u8). You absolutely can write your own async executor and for example interface it with some other subsystem (os or userland). As an example this was done in zigler to create ‘yielding FFI’ that interleaves zig async into yield points that the erlang virtual machine expects. This is impossible without language level async.

timestamped deep-dive:

[![](https://ziggit.dev/uploads/default/original/2X/2/22d1d1ed75078ef06ccf2a3cbdba6f4e757501b5.jpeg "ElixirConf 2021 - Isaac Yonemoto - Zig (heart) Elixir") ](https://www.youtube.com/watch?v=lDfjdGva3NE&t=2069s)

[Previous page](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715.md?page=2)

[Next page](https://ziggit.dev/t/what-is-the-status-of-async-with-zig/5715.md?page=4)
