From b4334031ccb98db1fb2c7d16959961dfc1d61b17 Mon Sep 17 00:00:00 2001 From: zzstoatzz Date: Sat, 4 Apr 2026 11:09:58 -0500 Subject: [PATCH] fix GC thread teardown race: join instead of detach, mark DB success from gc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - keep gc_thread handle and join it during shutdown before dp.deinit() runs — dp is stack-owned, detaching left a use-after-free window - add markDbSuccess() call at end of gc() so the health signal isn't solely dependent on uidForDid (event ingestion path) Co-Authored-By: Claude Opus 4.6 --- src/event_log.zig | 2 ++ src/main.zig | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/event_log.zig b/src/event_log.zig index eac0bdc..2d0f605 100644 --- a/src/event_log.zig +++ b/src/event_log.zig @@ -835,6 +835,8 @@ pub const DiskPersist = struct { if (self.max_dir_bytes > 0) { try self.gcBySize(); } + + self.markDbSuccess(); } /// delete oldest event files until total directory size is under max_dir_bytes diff --git a/src/main.zig b/src/main.zig index d963868..aaddac6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -297,7 +297,6 @@ pub fn main() !void { log.err("failed to start GC thread: {s}", .{@errorName(err)}); return err; }; - gc_thread.detach(); // wire HTTP fallback into broadcaster (all API endpoints served on WS port) var http_context = api.HttpContext{ @@ -366,7 +365,9 @@ pub fn main() !void { ws_listener.deinit(io); server_future.cancel(io); - // GC thread is detached and checks shutdown_flag — no cancel needed + // join GC thread — it checks shutdown_flag and will exit its sleep loop. + // must complete before dp.deinit() runs (dp is stack-owned). + gc_thread.join(); // cancel broadcaster fiber (shutdown flag already set, it will drain remaining) broadcast_future.cancel(io); -- 2.51.2