atproto relay implementation in zig zlay.waow.tech
9
fork

Configure Feed

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

fix pingLoop use-after-free: respect cancellation, check isClosed

pingLoop swallowed error.Canceled from io.sleep(), preventing
ping_future.cancel() from stopping the task before client.deinit()
freed the stream/TLS buffers. next writeFrame hit freed memory → GPF.

two changes:
- io.sleep() catch {} → catch return (cancellation-cooperative)
- check client.isClosed() before writeFrame (defense-in-depth)

also bumps zat to v0.3.0-alpha.11 and websocket.zig to 104608b.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

zzstoatzz 6d6c832c 59ff078f

+10 -7
+4 -4
build.zig.zon
··· 5 5 .minimum_zig_version = "0.16.0", 6 6 .dependencies = .{ 7 7 .zat = .{ 8 - .url = "https://tangled.org/zat.dev/zat/archive/v0.3.0-alpha.10.tar.gz", 9 - .hash = "zat-0.3.0-alpha.10-5PuC7nVhBQBinQH3IMZnhsHpL0OJcYhF4p3Wfo7OYAdX", 8 + .url = "https://tangled.org/zat.dev/zat/archive/v0.3.0-alpha.11.tar.gz", 9 + .hash = "zat-0.3.0-alpha.11-5PuC7nVhBQCNUnJEi_YUqQK6V8bbZacA-QN54nUunu4K", 10 10 }, 11 11 .websocket = .{ 12 - .url = "https://github.com/zzstoatzz/websocket.zig/archive/4222f98.tar.gz", 13 - .hash = "websocket-0.1.0-ZPISdQTUAwDJt7jFEbYJhdqRBztn8mHIFh-dNK5dOlxL", 12 + .url = "https://github.com/zzstoatzz/websocket.zig/archive/104608b.tar.gz", 13 + .hash = "websocket-0.1.0-ZPISdXjUAwC3rN7rT5NMG8HQJRug1NOboVWeX09SvSGv", 14 14 }, 15 15 .pg = .{ 16 16 .url = "git+https://github.com/zzstoatzz/pg.zig?ref=dev#5ce2355b1d851075523709c7d3068dcdb0224322",
+6 -3
src/subscriber.zig
··· 367 367 fn pingLoop(client: *websocket.Client, self: *Subscriber) void { 368 368 var fail_count: u32 = 0; 369 369 while (!self.shouldStop()) { 370 - // sleep in 1s increments so we can check shutdown 370 + // sleep in 1s increments so we can check shutdown. 371 + // return on any sleep error — critically, error.Canceled from 372 + // ping_future.cancel() must not be swallowed, otherwise deinit 373 + // frees the client while we're still running. 371 374 var elapsed: u32 = 0; 372 375 while (elapsed < ping_interval_sec and !self.shouldStop()) { 373 - self.io.sleep(Io.Duration.fromSeconds(1), .awake) catch {}; 376 + self.io.sleep(Io.Duration.fromSeconds(1), .awake) catch return; 374 377 elapsed += 1; 375 378 } 376 - if (self.shouldStop()) return; 379 + if (self.shouldStop() or client.isClosed()) return; 377 380 378 381 client.writeFrame(.ping, &.{}) catch { 379 382 fail_count += 1;