std.Io #
0.16 overhauls I/O with the std.Io interface. everything that can block (filesystem, networking, timers, concurrency) moves through this interface.
sources #
- std library docs (JS-rendered, use browser to read)
- devlog 2025-2026 — design rationale from Andrew Kelley
- codeberg source —
lib/std/Io.zig,lib/std/Io/Threaded.zig - kristoff.it, andrewkelley.me, porting guide
- tpuf — src/Client.zig retry loop (sleep cancellation, IoSource jitter, Retry-After on the real clock), 2026-09
the interface #
like Allocator for memory, Io is passed to functions that do I/O:
fn fetchData(io: std.Io, allocator: Allocator) ![]u8 {
// io provides networking, timers, concurrency, etc.
}
this decouples code from execution model. from the devlog (Oct 2025):
Regardless of whether Io is implemented via threads, or via an event loop, this code behaves optimally. The code also works when using single-threaded, blocking Io even though the operations happen sequentially.
same code, three execution models. write once, swap backend at init.
what Io provides #
- file system, networking, processes
- time and sleeping
- randomness
async,await,concurrent, andcancel- concurrent queues (
Io.Queue) - wait groups and select (
Io.Group,Io.Select) - mutexes, futexes, events, and conditions (
Io.Mutex,Io.Condition,Io.Event) - memory mapped files
timers and retry loops #
Retry loops should sleep through the same std.Io value that performed the
network request:
try io.sleep(std.Io.Duration.fromMilliseconds(delay_ms), .awake);
This makes the delay cancellation-aware and keeps the code backend-agnostic.
Use .awake for monotonic elapsed retry delays; use .real only when the
deadline is tied to wall-clock time. If a protocol exposes an absolute Unix
timestamp, convert it deliberately instead of mixing wall-clock and elapsed
duration logic.
sleep returns error.Canceled. catch {} on it turns a cancellation into
an immediate re-send; propagate it with try so the loop unwinds instead.
std.Io also provides randomness. Retry jitter can use std.Random.IoSource
so the policy stays on the caller's I/O backend instead of reaching for a
separate global source:
const source: std.Random.IoSource = .{ .io = io };
const delay = half + source.interface().uintAtMost(u32, half); // equal jitter
A Retry-After header is seconds from now, so compare it on the wall
clock: Io.Clock.real.now(io).toSeconds().
backends #
Io.Threaded— thread-based, always available, production defaultIo.Evented— fiber-based, experimental:- linux:
Io.Uring(io_uring) - macOS/iOS:
Io.Dispatch(GCD) - BSD:
Io.Kqueue - unsupported platforms:
void - uses userspace stack switching (fibers/green threads)
- currently experimental — known bugs (see patterns.md)
- linux:
- WASM: fiber-based backends can't work (no stack switching). stackless coroutines planned as future compiler feature.
backend selection:
const Backend = if (Io.Evented != void) Io.Evented else Io.Threaded;
init differs by backend:
// Threaded
backend = Io.Threaded.init(allocator, .{});
// Evented
try Backend.init(&backend, allocator, .{});
both expose .io() → std.Io. all downstream code uses the same Io value.
no function coloring (mostly) #
async and await are library functions, not keywords. no viral async/await infection.
fn foo(io: std.Io) !void { ... }
// called normally
try foo(io);
// or asynchronously
const future = io.async(foo, .{io});
caveat: coloring shifted from keyword-based to parameter-based. if a pure computation function later needs I/O, it must accept an Io parameter, which propagates to callers. the advantage: code stays agnostic to the execution model.
files in this folder #
- concurrency.md — async vs concurrent, Future, Group, Select, Queue. read before building any timeout/watchdog on
io.async: it degrades to an inline call pastasync_limit(= cpus − 1, so always inline on a 1-vCPU box), which silently disables deadlines built by racing a future. - synchronization.md — Mutex, Condition, CancelProtection, cancellation model
- patterns.md — backend selection, InitOptions, debug_io, long-lived tasks, timedWait workarounds
- fibers.md — building stackful fibers: the ReleaseSafe clobber bugs, guard-page stacks, keeping blocking work off fibers