# Async Status in Zig 0.16

### Async Status in Zig 0.16

Zig once had async functions as a language feature.

In Zig 0.16, async function syntax is not part of the stable language model. Code should not be built around old examples that use:

```zig
async
await
suspend
resume
```

For ordinary programs, use one of these instead:

| Need | Use |
|---|---|
| Run work in parallel | `std.Thread` |
| Protect shared state | `std.Thread.Mutex` |
| Wait for state changes | `std.Thread.Condition` |
| Count or signal resources | `std.Thread.Semaphore` |
| Do one small shared operation | `std.atomic` |
| Build evented I/O | explicit event loop or library design |

This matters because old Zig examples may still appear online. They may describe async frames, suspension points, or `await`. Such examples belong to older Zig versions and should not be copied into Zig 0.16 code.

A simple concurrent Zig program today is usually written with threads:

```zig
const std = @import("std");

fn worker(id: u32) void {
    std.debug.print("worker {d}\n", .{id});
}

pub fn main() !void {
    const a = try std.Thread.spawn(.{}, worker, .{1});
    const b = try std.Thread.spawn(.{}, worker, .{2});

    a.join();
    b.join();
}
```

This creates two operating system threads. Each runs `worker`.

For waiting on an event, use synchronization:

```zig
const std = @import("std");

const State = struct {
    mutex: std.Thread.Mutex = .{},
    condition: std.Thread.Condition = .{},
    ready: bool = false,
};

fn producer(state: *State) void {
    state.mutex.lock();
    defer state.mutex.unlock();

    state.ready = true;
    state.condition.signal();
}

fn consumer(state: *State) void {
    state.mutex.lock();
    defer state.mutex.unlock();

    while (!state.ready) {
        state.condition.wait(&state.mutex);
    }

    std.debug.print("ready\n", .{});
}

pub fn main() !void {
    var state = State{};

    const a = try std.Thread.spawn(.{}, consumer, .{&state});
    const b = try std.Thread.spawn(.{}, producer, .{&state});

    a.join();
    b.join();
}
```

This is explicit. The state is visible. The lock is visible. The wait is visible.

That is the current Zig style.

Async I/O is still possible as a library design. It is not magic syntax. A library may use nonblocking file descriptors, OS event systems, callbacks, queues, or worker threads.

On Linux, this may involve `epoll` or `io_uring`.

On BSD and macOS, it may involve `kqueue`.

On Windows, it may involve IOCP.

Zig does not require one model for all programs. A program can choose the model that fits the problem.

This keeps the language small.

For many tools, blocking I/O is enough.

For example, a file copy program can read and write in a loop:

```zig
const std = @import("std");

pub fn main() !void {
    var stdout_buffer: [1024]u8 = undefined;
    var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
    const stdout = &stdout_writer.interface;

    try stdout.print("copy would go here\n", .{});
    try stdout.flush();
}
```

For a server handling many connections, one thread per connection may be too expensive. Then the program needs an event loop, a thread pool, or both.

The important point is that Zig 0.16 does not hide this choice behind async syntax.

You choose the concurrency model directly.

Exercise 18-17. Find an old Zig async example and identify the syntax that no longer belongs in Zig 0.16 code.

Exercise 18-18. Rewrite a simple `async` style example using `std.Thread.spawn`.

Exercise 18-19. Write a program that uses a condition variable instead of a busy loop.

Exercise 18-20. Write down which model you would use for a command-line tool, a file server, and a CPU-bound worker program.

