atproto utils for zig zat.dev
atproto sdk zig
26
fork

Configure Feed

Select the types of activity you want to include in your feed.

devlog 008: fix timeline, links, version, add doc citations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+10 -10
+10 -10
devlog/008-the-io-migration.md
··· 4 4 5 5 ## what changed in 0.16 6 6 7 - `std.Io` is a backend-agnostic interface for all I/O and concurrency. two backends: 7 + [`std.Io`](https://ziglang.org/documentation/master/std/#std.Io) is a backend-agnostic interface for all I/O and concurrency. two backends: 8 8 9 - - **Threaded** — always available. `io.concurrent()` spawns OS threads. 10 - - **Evented** — fiber-based. io_uring on linux, GCD on macOS, kqueue on BSD. `io.concurrent()` creates cheap userspace coroutines. 9 + - [**Threaded**](https://ziglang.org/documentation/master/std/#std.Io.Threaded) — always available. `io.concurrent()` spawns OS threads. 10 + - [**Evented**](https://ziglang.org/documentation/master/std/#std.Io.Evented) — fiber-based. [io_uring on linux, GCD on macOS, kqueue on BSD](https://ziglang.org/devlog/2026/#2026-02-13). `io.concurrent()` creates cheap userspace coroutines. 11 11 12 - same code runs on both. you write against `Io`, pick the backend at init, and the scheduler does the rest. `io.async()` for CPU work (bounded pool, overflow runs inline). `io.concurrent()` for I/O tasks (unbounded under Threaded, fibers under Evented). `io.sleep()` is cancellation-aware. `Io.Mutex` integrates with the scheduler's futex. 12 + same code runs on both. you write against `Io`, pick the backend at init, and the scheduler does the rest. [`io.async()`](https://ziglang.org/documentation/master/std/#std.Io.async) for CPU work (may run inline if no concurrency available). [`io.concurrent()`](https://ziglang.org/documentation/master/std/#std.Io.concurrent) for I/O tasks (OS threads under Threaded, fibers under Evented). `io.sleep()` is cancellation-aware. [`Io.Mutex`](https://ziglang.org/documentation/master/std/#std.Io.Mutex) integrates with the scheduler's futex. 13 13 14 14 the promise: write once, switch backends, get threads or fibers for free. 15 15 ··· 52 52 53 53 ## the relay migration 54 54 55 - [zlay](https://tangled.sh/zzstoatzz.io/zlay) is an AT Protocol relay — ~8,400 lines of zig, ~2,750 PDS subscribers, WebSocket fan-out to downstream consumers. it was the heaviest consumer of the 0.15 API surface. migrating it to 0.16 compiled on the first try. 55 + [zlay](https://tangled.org/zzstoatzz.io/zlay) is an AT Protocol relay — ~8,400 lines of zig, ~2,750 PDS subscribers, WebSocket fan-out to downstream consumers. it was the heaviest consumer of the 0.15 API surface. migrating it to 0.16 compiled on the first try. 56 56 57 57 nine crashes followed. 58 58 ··· 222 222 223 223 the logs showed nothing unusual. chain breaks (expected after restarts when cursor positions are stale), normal reconnection cycles, then sudden death. 13 restarts in 12 hours. 224 224 225 - we had a separate observation that was shaping our thinking: a minimal repro (`scripts/repro_evented.zig`) that spawns a single fiber and returns GPFs immediately under ReleaseSafe. the crash lands in `fiber.zig:contextSwitch` → `Uring.zig:mainIdle`. so we had a confirmed fiber context-switch bug under one build mode, and a mystery SIGSEGV under another. the natural conclusion: same bug, different manifestation. ReleaseFast just hides it longer because the optimizer arranges code differently. 225 + we had a separate observation that was shaping our thinking: a minimal repro ([`repro_evented.zig`](https://tangled.org/zzstoatzz.io/zlay/tree/main/scripts/repro_evented.zig)) that spawns a single fiber and returns GPFs immediately under ReleaseSafe. the crash lands in `fiber.zig:contextSwitch` → `Uring.zig:mainIdle`. so we had a confirmed fiber context-switch bug under one build mode, and a mystery SIGSEGV under another. the natural conclusion: same bug, different manifestation. ReleaseFast just hides it longer because the optimizer arranges code differently. 226 226 227 227 we spent time investigating the fiber machinery, reading disassembly of the context switch, checking for upstream fixes (fiber.zig was unchanged across 32 dev builds). we considered patching the context switch ourselves. we checked upstream Uring networking implementation status — still fully stubbed. we read the zig team's position on Evented — "experimental," "important followup work to be done." 228 228 ··· 248 248 if (line_start > pos) break; // ← TCP split mid-CRLF, read more 249 249 ``` 250 250 251 - this is the bug that ReleaseSafe would have caught on the first occurrence, with a stack trace pointing directly at the line. instead, we ran ReleaseFast for weeks, saw silent SIGSEGVs, and blamed the fiber scheduler. 251 + this is the bug that ReleaseSafe would have caught on the first occurrence, with a stack trace pointing directly at the line. instead, we ran ReleaseFast for days, saw silent SIGSEGVs, and blamed the fiber scheduler. 252 252 253 253 ## the ReleaseSafe problem 254 254 ··· 269 269 270 270 under ReleaseFast, there's a `lea` that loads the SwitchMessage stack address into `%rsi` before the asm. under ReleaseSafe, that `lea` appears to be missing — `%rsi` holds a stale value from a prior function call. the ReleaseSafe prologue adds stack probing (`__zig_probe_stack`) and a canary (`fs:0x28`), which change the code layout surrounding the inline asm. we think this is why the register allocation differs, but we're not certain — there may be something else going on. 271 271 272 - we've written this up as a [bug report](scripts/fiber_gpf_issue.md) with a standalone reproduction. 272 + we've written this up as a [bug report](https://tangled.org/zzstoatzz.io/zlay/tree/main/scripts/fiber_gpf_issue.md) with a standalone reproduction. 273 273 274 274 this is a real problem for the ecosystem. ReleaseSafe is the mode designed for production services that want optimization with safety checks. TigerBeetle uses it. the zig compiler's own nightlies recently switched to it. Ghostty and Bun use ReleaseFast, but both have noted they'd prefer ReleaseSafe if the performance cost were lower. for `Io.Evented` to be a viable production backend, it needs to work with ReleaseSafe. 275 275 ··· 287 287 288 288 the thing that found it was switching to ReleaseSafe. not to fix the crash — we'd already reverted to Threaded for that — but because reverting happened to re-enable the build mode that had the safety checks. the bounds check caught the real bug on the first handshake that split on `\r\n`. 289 289 290 - there are two bugs here and they're both real. the websocket off-by-one was the production crash. the ReleaseSafe GPF is a separate issue that blocks Evented from running with safety checks. we're filing the latter upstream. in the meantime, ReleaseFast works, and we know what to look for when it doesn't. 290 + there are two bugs here and they're both real. the websocket off-by-one was the production crash. the ReleaseSafe GPF is a separate issue that blocks Evented from running with safety checks. we'd consider [filing the latter upstream](https://tangled.org/zzstoatzz.io/zlay/tree/main/scripts/fiber_gpf_issue.md). in the meantime, ReleaseFast works, and we know what to look for when it doesn't. 291 291 292 - zat is v0.3.0. the Io parameter is the only breaking change. 292 + zat is v0.3.0-alpha. the Io parameter is the only breaking change.