logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·31.10.26
← Back to news

Zig’s Io.Threaded is neat

Original article
matklad.github.io|134 points|78 comments|by chilipepperhott|Aug 21, 2026

Exploring the Elegance of Zig's Io.Threaded

The std.Io.Threaded implementation in Zig is a fascinating addition to the language's new Io interface, specifically designed to facilitate concurrency.

What makes it truly impressive is how it solves a long-standing challenge: the reliable cancellation of blocking system calls, achieving a result that is more robust than most existing implementations.

Concurrency vs. Parallelism

To understand why this matters, we must first distinguish between two often-confused concepts. Following the intuition of @tedinski:

ConceptFocusNature
ConcurrencyManaging asynchronous, non-deterministic events.Structural/Organizational
ParallelismUtilizing hardware to execute multiple tasks simultaneously.Resource-based/Execution

While these definitions are accurate, they don't immediately explain how to program them. Parallelism is generally declarative. For example, using rayon in Rust:

use rayon::prelude::*;

fn sum_of_squares(input: &[i32]) -> i32 {
    input.par_iter().map(|x| x * x).sum()
}

In this model, the programmer describes how to partition the problem into independent chunks. The platform then ensures the partitioning is non-racy, processes the chunks, and returns control.

The "Cancellation Wall"

The real difficulty arises in concurrency. When two asynchronous tasks run, one may discover that the other is no longer needed. At that moment, the second task must be actively canceled.

You cannot simply wait for it to finish because the task might be stuck waiting for a message that will never arrive. This leads to the common but flawed advice: "Just use threads."

While you can spawn many threads, this approach fails for two reasons:

  1. System Constraints: Spawning excessive threads often requires OS-level configuration changes.
  2. The Blocking Problem: You cannot easily cancel a thread blocked in the kernel.

Consider this common pattern:

while (true) {
    if (is_canceled()) return error.Canceled; 
    // This works for CPU-bound loops!
}

However, if the thread is stuck here: const read_size = try read(fd, buffer); The language API typically provides no way to "wake up" that thread from the kernel.

The Solution: Signals and Flags

Zig's Io.Threaded avoids "shiny" new APIs like io_uring and instead leverages a clever use of standard OS threads and signals.

The Unix Protocol

On Unix-like systems, the kernel can interrupt a blocking syscall via signals. When a signal is delivered, the syscall wakes up and returns EINTR.

Since signals are racy (they might arrive too early or too late), Zig uses a specific handshake protocol:

In Zig, this manifests to the user as error.Canceled.

The Windows Approach

Windows provides a more straightforward mechanism: NtCancelSynchronousIoFile. The author notes that between Job objects, IO Completion Ports (IOCP), and fibers, the NT kernel generally has a more cohesive concurrency story than Unix.

Comparative Analysis

Zig vs. Java

Java features a thread interruption mechanism, but it has a critical flaw: it does not interrupt blocking syscalls. In Java, IOException and InterruptedException are distinct; therefore, a thread blocked on IO cannot be interrupted.

The "Twilight Zone" of Concurrency

Concurrency exists in a "twilight zone" between the kernel, the runtime, and the language.

  • The CPU provides almost no native concurrency (outside of interrupts).
  • The Kernel/Libc traditionally handle it, which often hampers language design.
  • pthread_cancel is too blunt—it destroys the entire thread. Since threads are expensive to create, pooling is preferred, making total destruction undesirable.

Zig's Architectural Innovation

Zig solves these issues by separating the intent of the execution at the interface level. It distinguishes between:

  • io.async: May run concurrently.
  • io.concurrent: Must run concurrently.

This is conceptually similar to the std::launch policy in C++ (Effective Modern C++ Item 36). By explicitly naming these states, Zig provides more precise type signatures: concurrent  ⟹  Fallible∣async  ⟹  Infallible\text{concurrent} \implies \text{Fallible} \quad | \quad \text{async} \implies \text{Infallible}concurrent⟹Fallible∣async⟹Infallible

Final Implementation Notes

  • Reader/Writer interfaces erase errors to support cancellation.
  • Full integration with try and defer for post-cancelation cleanup (still requires manual care/flushing).

By treating cancellation not as a "failure" but as a fundamental part of error management (where an error is essentially a cancellation+payload\text{cancellation} + \text{payload}cancellation+payload), Zig creates a powerful, predictable system for concurrent IO.