From 3838de4e5956ac2a068ada45cb42a4a9a01c8a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Thu, 19 Feb 2026 10:43:13 +0000 Subject: [PATCH] sema: use real zig caches and fix thread pool lifetime - Use Directories.init() to resolve ~/.cache/zig (global) and .zig-cache (local) instead of a temp dir, re-using the same resolution logic as src/main.zig (introspect module). - Point zig_lib at zig-out/lib/zig/ (installed copy) instead of lib/ (source tree) to avoid "file exists in modules 'root' and 'std'" when compiling files that live under lib/. - Heap-allocate the thread pool to keep its address stable. Worker threads reference the Pool's condvar/mutex; a by-value copy into ZigSemaResult left them waiting on the original stack address while deinit broadcast on the copy, causing a deadlock. Co-Authored-By: Claude Opus 4.6 --- stage0/sema.zig | 54 ++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/stage0/sema.zig b/stage0/sema.zig index 44b9119fdf..f85dc39e7b 100644 --- a/stage0/sema.zig +++ b/stage0/sema.zig @@ -8,19 +8,19 @@ const Compilation = zig_internals.Compilation; const Package = zig_internals.Package; /// Result of running the real Zig sema pipeline via Compilation. -/// Owns the Compilation, Directories, thread pool, temp dir, and arena. +/// Owns the Compilation, Directories, thread pool, and arena. pub const ZigSemaResult = struct { comp: *Compilation, dirs: Compilation.Directories, - tmp_dir: std.testing.TmpDir, arena_state: std.heap.ArenaAllocator, - thread_pool: std.Thread.Pool, + thread_pool: *std.Thread.Pool, + gpa: Allocator, pub fn deinit(self: *ZigSemaResult) void { self.comp.destroy(); self.dirs.deinit(); self.thread_pool.deinit(); - self.tmp_dir.cleanup(); + self.gpa.destroy(self.thread_pool); self.arena_state.deinit(); } }; @@ -32,25 +32,18 @@ pub fn zigSema(gpa: Allocator, src_path: []const u8) !ZigSemaResult { errdefer arena_state.deinit(); const arena = arena_state.allocator(); - // Set up temp dir for Compilation cache. - var tmp_dir = std.testing.tmpDir(.{}); - errdefer tmp_dir.cleanup(); - - // Resolve paths. - const cwd_path = try std.fs.cwd().realpathAlloc(arena, "."); - const zig_lib_path = try std.fs.path.join(arena, &.{ cwd_path, "lib" }); - const zig_lib_handle = try std.fs.cwd().openDir("lib", .{}); - const tmp_path = try tmp_dir.dir.realpathAlloc(arena, "."); - const cache_path = try std.fmt.allocPrint(arena, "{s}/.cache", .{tmp_path}); - try tmp_dir.dir.makeDir(".cache"); - const cache_handle = try tmp_dir.dir.openDir(".cache", .{}); - - var dirs = Compilation.Directories{ - .cwd = cwd_path, - .zig_lib = .{ .path = zig_lib_path, .handle = zig_lib_handle }, - .global_cache = .{ .path = cache_path, .handle = cache_handle }, - .local_cache = .{ .path = cache_path, .handle = cache_handle }, - }; + // Use the real Zig cache directories: ~/.cache/zig (global) and .zig-cache (local). + // Point zig_lib at zig-out/lib/zig/ (the installed copy) rather than lib/ (the source + // tree) to avoid "file exists in modules 'root' and 'std'" when compiling source files + // that live under lib/. + var dirs: Compilation.Directories = .init( + arena, + "zig-out/lib/zig", + null, + .search, + {}, + "", + ); errdefer dirs.deinit(); // Hardcode x86_64-linux-musl target. @@ -96,14 +89,21 @@ pub fn zigSema(gpa: Allocator, src_path: []const u8) !ZigSemaResult { .parent = null, }); - var thread_pool: std.Thread.Pool = undefined; + // Heap-allocate the thread pool so its address stays stable after zigSema + // returns. The worker threads hold references to the Pool's internal + // condvar/mutex; a by-value copy would leave them waiting on a stale address. + const thread_pool = try gpa.create(std.Thread.Pool); + thread_pool.* = undefined; try thread_pool.init(.{ .allocator = gpa, .n_jobs = 1, .track_ids = true, .stack_size = 60 << 20, }); - errdefer thread_pool.deinit(); + errdefer { + thread_pool.deinit(); + gpa.destroy(thread_pool); + } var create_diag: Compilation.CreateDiagnostic = undefined; const comp = Compilation.create(gpa, arena, &create_diag, .{ @@ -112,7 +112,7 @@ pub fn zigSema(gpa: Allocator, src_path: []const u8) !ZigSemaResult { .config = config, .root_mod = root_mod, .emit_bin = .no, - .thread_pool = &thread_pool, + .thread_pool = thread_pool, .cache_mode = .whole, }) catch |err| switch (err) { error.CreateFail => { @@ -135,8 +135,8 @@ pub fn zigSema(gpa: Allocator, src_path: []const u8) !ZigSemaResult { return .{ .comp = comp, .dirs = dirs, - .tmp_dir = tmp_dir, .arena_state = arena_state, .thread_pool = thread_pool, + .gpa = gpa, }; }