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 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 10:43:13 +00:00
parent 33e3e5475d
commit 3838de4e59

View File

@@ -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,
};
}