zio#
an evented std.Io backend for zig, built to beat thread-per-connection under
ReleaseSafe — with all safety checks on.
zig 0.16 shipped Io.Threaded as the only production backend; Io.Evented
is experimental, has no networking, and its fiber machinery crashes under
ReleaseSafe. zio fills that gap as an ordinary package — no compiler fork, no
stdlib patch.
design#
- ordinary package.
std.Iois a plain vtable; zio is just another implementation, the same wayIo.Threadedis. - hybrid by composition.
Ziowraps an innerIo.Threadedand delegates everything to it except sockets + sleep + concurrency, which run on an evented engine. anything unimplemented falls back to a thread — graceful degradation by construction. - ReleaseSafe-first. the engine core is a readiness loop (epoll/kqueue) driving per-connection state machines — no fibers, no stack switching, nothing the safety checker can object to. fibers are a measured upgrade behind a build option, adopted only if the scoreboard says they win.
- scoreboard before runtime.
Io.Threadedis the permanent reference column. no engine code lands without a benchmark delta.
surfaces#
zio.Engine— the readiness-loop core: register non-blocking fds with handlers, run one engine per core. callback-style.zio.Zio— the hybridstd.Ioimplementation (delegating skeleton today; evented ops splice in as the engine grows).
benchmarks#
methodology in docs/benchmarks.md, results in docs/baselines.md. headline (linux, ReleaseSafe, 2,800 conns): ~200× less RSS than thread-per-conn, O(cores) threads instead of O(conns), higher throughput, and a full 2,800-connection reconnect storm re-established in ~115 ms.
zig build bench -Doptimize=ReleaseSafe -- --backend zio --conns 2800 --seconds 5 --storm
status#
- bench harness +
Io.Threadedbaselines - hybrid delegating skeleton (
ZiowrappingIo.Threaded) - engine v0: readiness loops (epoll/kqueue), per-conn state, reconnect storms, interest modification, non-blocking connect
- fiber gauntlet: ReleaseSafe stack-switch crashes root-caused (LLVM #167783 x30-clobber bug + missing x18 clobber on linux) and fixed in the vendored primitive — 7/7 on all three platforms, Debug and ReleaseSafe (docs/fiber-gauntlet-2026-07-09.md)
- fiber scheduler v1 (
zio.Sched): blocking-style read/write/connect/ accept/sleep on one os thread; one-shot poller arming, lazy gen-tagged timers, spurious-wakeup-safe parks, fiber recycling. try it:zig build demo -Doptimize=ReleaseSafe -- --fibers 2000 --seconds 10 - fiber-backed std.Io vtable (
zio.ZioBackend): concurrent/await/cancel spawn fibers; net connect/accept/read/write park on readiness; sleep on sched timers; fiber-aware futexWait/Wake makes Io.Mutex/Condition/ Queue correct across fiber and thread domains; netLookup spills DNS to an inner-Threaded thread. unmodified Io.net code runs on fibers. cancellation reaches fibers parked on anything (io/futex/timer/await) promptly via an all-fibers interrupt registry. adoption: docs/adoption-guide.md - mmap fiber stacks with guard pages (malloc stacks commit ~256KB/fiber — the demo's RSS is all stacks)
- io_uring engine + runtime detection with epoll fallback (default docker seccomp blocks io_uring)
- first production consumer integration
origins#
zio came out of running ~2,800 production websocket subscriptions on both
Io.Threaded and Io.Evented and watching the experimental backend fail in
ways that forced a retreat to thread-per-connection. the write-ups that led
here (retrospective, stdlib empirics, plan) live with that project's docs;
the short version is in docs/benchmarks.md.