From e51fd6728fcc4dd93e3bd1ddb2e4ca96a8082b12 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 20 Jun 2024 19:44:07 -0700 Subject: [PATCH] new thread pool jobserver integration std.Thread.Pool: back to spawning all threads in initialization because it's overall simpler. This scheme requires init to be passed a pointer to the struct. std.process.Child: implement integration with thread pool jobserver. The environment variable is called `JOBSERVERV2`. The API works based on assigning a thread pool to the child process. build runner: store the thread pool in std.Build.Graph so that it can be passed to child processes during the make phase. Fix not allocating +1 pollfds in previous commit. --- lib/compiler/build_runner.zig | 14 ++++-- lib/std/Build.zig | 2 + lib/std/Build/Step.zig | 7 ++- lib/std/Build/Step/Run.zig | 1 + lib/std/Thread/Pool.zig | 86 +++++++++++------------------------ lib/std/process.zig | 79 ++++++++++++++++++++++++++++---- lib/std/process/Child.zig | 28 ++++++++++++ lib/std/zig.zig | 16 ++++--- src/Compilation.zig | 2 + src/link.zig | 1 + src/main.zig | 20 ++++++-- 11 files changed, 171 insertions(+), 85 deletions(-) diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index ac0f0a370f3616264b7cd7c1ae802851455c41e5..17394e2186b1ef1dc7226fb9cef2cc88621873e2 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -74,6 +74,7 @@ pub fn main() !void { .query = .{}, .result = try std.zig.system.resolveTargetQuery(.{}), }, + .thread_pool = undefined, }; graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); @@ -92,6 +93,7 @@ pub fn main() !void { var targets = ArrayList([]const u8).init(arena); var debug_log_scopes = ArrayList([]const u8).init(arena); var thread_pool_options: std.zig.ThreadPoolOptions = .{ + .allocator = arena, .cache_directory = local_cache_directory, }; @@ -448,7 +450,8 @@ fn runStepNames( } } - var thread_pool = try std.zig.initThreadPool(gpa, thread_pool_options); + const thread_pool = &b.graph.thread_pool; + try std.zig.initThreadPool(thread_pool, thread_pool_options); defer thread_pool.deinit(); { @@ -469,7 +472,7 @@ fn runStepNames( if (step.state == .skipped_oom) continue; thread_pool.spawnWg(&wait_group, workerMakeOneStep, .{ - &wait_group, &thread_pool, b, step, step_prog, run, + &wait_group, b, step, step_prog, run, }); } } @@ -890,12 +893,13 @@ fn constructGraphAndCheckForDependencyLoop( fn workerMakeOneStep( wg: *std.Thread.WaitGroup, - thread_pool: *std.Thread.Pool, b: *std.Build, s: *Step, prog_node: std.Progress.Node, run: *Run, ) void { + const thread_pool = &b.graph.thread_pool; + // First, check the conditions for running this step. If they are not met, // then we return without doing the step, relying on another worker to // queue this step up again when dependencies are met. @@ -975,7 +979,7 @@ fn workerMakeOneStep( // Successful completion of a step, so we queue up its dependants as well. for (s.dependants.items) |dep| { thread_pool.spawnWg(wg, workerMakeOneStep, .{ - wg, thread_pool, b, dep, prog_node, run, + wg, b, dep, prog_node, run, }); } } @@ -1000,7 +1004,7 @@ fn workerMakeOneStep( remaining -= dep.max_rss; thread_pool.spawnWg(wg, workerMakeOneStep, .{ - wg, thread_pool, b, dep, prog_node, run, + wg, b, dep, prog_node, run, }); } else { run.memory_blocked_steps.items[i] = dep; diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 942a8af406f21ebb87105d96f80eee142431da97..2153ef11bd9a2871ac53e665d86f3c44de4a029e 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -120,6 +120,8 @@ pub const Graph = struct { needed_lazy_dependencies: std.StringArrayHashMapUnmanaged(void) = .{}, /// Information about the native target. Computed before build() is invoked. host: ResolvedTarget, + /// Uninitialized until the make phase. + thread_pool: std.Thread.Pool, }; const AvailableDeps = []const struct { []const u8, []const u8 }; diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 0813aba6897f3d08ebee139c5de2d756b9d05675..94aa3a8afc8f8b4a8b275cbd021b0f99dd993cb7 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -283,15 +283,17 @@ pub fn captureChildProcess( progress_node: std.Progress.Node, argv: []const []const u8, ) !std.process.Child.RunResult { - const arena = s.owner.allocator; + const b = s.owner; + const arena = b.allocator; try handleChildProcUnsupported(s, null, argv); - try handleVerbose(s.owner, null, argv); + try handleVerbose(b, null, argv); const result = std.process.Child.run(.{ .allocator = arena, .argv = argv, .progress_node = progress_node, + .thread_pool = &b.graph.thread_pool, }) catch |err| return s.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); if (result.stderr.len > 0) { @@ -334,6 +336,7 @@ pub fn evalZigProcess( child.stderr_behavior = .Pipe; child.request_resource_usage_statistics = true; child.progress_node = prog_node; + child.thread_pool = &b.graph.thread_pool; child.spawn() catch |err| return s.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err), diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 69d6b393fd4191864ee7e6963fd505d2e9cb87a8..b11ae6e44b15a3ec6a55c756751e31f3d6315b3a 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -1246,6 +1246,7 @@ fn spawnChildAndCollect( if (run.stdio != .zig_test and !run.disable_zig_progress and !inherit) { child.progress_node = prog_node; } + child.thread_pool = &b.graph.thread_pool; const term, const result, const elapsed_ns = t: { if (inherit) std.debug.lockStdErr(); diff --git a/lib/std/Thread/Pool.zig b/lib/std/Thread/Pool.zig index 263a9aa96460cffc275563d9e8bc150c0eaab1e9..dab241fdb869663e90d569b95cc8627d3cead610 100644 --- a/lib/std/Thread/Pool.zig +++ b/lib/std/Thread/Pool.zig @@ -7,11 +7,9 @@ const assert = std.debug.assert; mutex: std.Thread.Mutex, cond: std.Thread.Condition, run_queue: RunQueue, -run_queue_len: usize, end_flag: bool, allocator: std.mem.Allocator, -threads_buffer: []std.Thread, -threads_len: usize, +threads: []std.Thread, job_server_options: Options.JobServer, job_server: ?*JobServer, @@ -23,6 +21,9 @@ const Runnable = struct { const RunProto = *const fn (*Runnable) void; pub const Options = struct { + /// Not required to be thread-safe; protected by the pool's mutex. + allocator: std.mem.Allocator, + /// Max number of threads to be actively working at the same time. /// /// `null` means to use the logical core count, leaving the main thread to @@ -49,22 +50,16 @@ pub const Options = struct { }; }; -/// After initializing the thread pool and spawning work, the main thread must -/// call `waitAndWork`. -pub fn init( - /// Not required to be thread-safe; protected by the pool's mutex. - allocator: std.mem.Allocator, - options: Options, -) !Pool { - var pool: Pool = .{ +pub fn init(pool: *Pool, options: Options) !void { + const allocator = options.allocator; + + pool.* = .{ .mutex = .{}, .cond = .{}, .run_queue = .{}, - .run_queue_len = 0, .end_flag = false, .allocator = allocator, - .threads_buffer = &.{}, - .threads_len = 0, + .threads = &.{}, .job_server_options = options.job_server, .job_server = null, }; @@ -75,8 +70,15 @@ pub fn init( const thread_count = options.n_jobs orelse @max(1, std.Thread.getCpuCount() catch 1); assert(thread_count > 0); - pool.threads_buffer = try allocator.alloc(std.Thread, thread_count); - errdefer allocator.free(pool.threads_buffer); + // Kill and join any threads we spawned and free memory on error. + pool.threads = try allocator.alloc(std.Thread, thread_count); + var spawned: usize = 0; + errdefer pool.join(spawned); + + for (pool.threads) |*thread| { + thread.* = try std.Thread.spawn(.{}, worker, .{pool}); + spawned += 1; + } switch (options.job_server) { .abstain, .connect => {}, @@ -84,7 +86,7 @@ pub fn init( var server = try addr.listen(.{}); errdefer server.deinit(); - const pollfds = try allocator.alloc(std.posix.pollfd, thread_count); + const pollfds = try allocator.alloc(std.posix.pollfd, thread_count + 1); errdefer allocator.free(pollfds); const job_server = try allocator.create(JobServer); @@ -99,11 +101,14 @@ pub fn init( pool.job_server = job_server; }, } - - return pool; } pub fn deinit(pool: *Pool) void { + pool.join(pool.threads.len); + pool.* = undefined; +} + +fn join(pool: *Pool, spawned: usize) void { if (builtin.single_threaded) return; @@ -127,15 +132,10 @@ pub fn deinit(pool: *Pool) void { job_server.thread.join(); } - // Since we set end_flag with the mutex locked, no more threads could have - // been created. - const threads = pool.threads_buffer[0..pool.threads_len]; - - for (threads) |thread| + for (pool.threads[0..spawned]) |thread| thread.join(); - pool.allocator.free(pool.threads_buffer); - pool.* = undefined; + pool.allocator.free(pool.threads); } pub const JobServer = struct { @@ -256,22 +256,6 @@ pub fn spawnWg(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, args }; pool.run_queue.prepend(&closure.run_node); - pool.run_queue_len += 1; - - // If there was already any queued work, spawn a new thread if we are - // under the max. - if (pool.run_queue_len > 1 and pool.threads_len < pool.threads_buffer.len) { - if (std.Thread.spawn(.{}, worker, .{pool})) |new_thread| { - pool.threads_buffer[pool.threads_len] = new_thread; - pool.threads_len += 1; - } else |_| if (pool.threads_len == 0) { - pool.mutex.unlock(); - @call(.auto, func, args); - wait_group.finish(); - return; - } - } - pool.mutex.unlock(); } @@ -319,21 +303,6 @@ pub fn spawn(pool: *Pool, comptime func: anytype, args: anytype) void { }; pool.run_queue.prepend(&closure.run_node); - pool.run_queue_len += 1; - - // If there was already any queued work, spawn a new thread if we are - // under the max. - if (pool.run_queue_len > 1 and pool.threads_len < pool.threads_buffer.len) { - if (std.Thread.spawn(.{}, worker, .{pool})) |new_thread| { - pool.threads_buffer[pool.threads_len] = new_thread; - pool.threads_len += 1; - } else |_| if (pool.threads_len == 0) { - pool.mutex.unlock(); - @call(.auto, func, args); - return; - } - } - pool.mutex.unlock(); } @@ -351,8 +320,6 @@ fn worker(pool: *Pool) void { while (true) { while (pool.run_queue.popFirst()) |run_node| { - pool.run_queue_len -= 1; - // Temporarily unlock the mutex in order to execute the run_node. pool.mutex.unlock(); defer pool.mutex.lock(); @@ -391,7 +358,6 @@ pub fn waitAndWork(pool: *Pool, wait_group: *WaitGroup) void { defer pool.mutex.unlock(); break :blk pool.run_queue.popFirst(); }) |run_node| { - pool.run_queue_len -= 1; run_node.data.runFn(&run_node.data); continue; } diff --git a/lib/std/process.zig b/lib/std/process.zig index 787831e61cd43ad09370c91ad6da6279a1d0ad36..3ea08b72e34d63088aafb514ceab585c5fd39b35 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1816,6 +1816,14 @@ pub const CreateEnvironOptions = struct { /// If non-null, negative means to remove the environment variable, and >= 0 /// means to provide it with the given integer. zig_progress_fd: ?i32 = null, + + job_server_path: String = .unchanged, + + pub const String = union(enum) { + unchanged, + deleted, + updated: []const u8, + }; }; /// Creates a null-deliminated environment variable block in the format @@ -1825,8 +1833,8 @@ pub fn createEnvironFromMap( map: *const EnvMap, options: CreateEnvironOptions, ) Allocator.Error![:null]?[*:0]u8 { - const ZigProgressAction = enum { nothing, edit, delete, add }; - const zig_progress_action: ZigProgressAction = a: { + const EnvVarAction = enum { nothing, edit, delete, add }; + const zig_progress_action: EnvVarAction = a: { const fd = options.zig_progress_fd orelse break :a .nothing; const contains = map.get("ZIG_PROGRESS") != null; if (fd >= 0) { @@ -1836,6 +1844,11 @@ pub fn createEnvironFromMap( } break :a .nothing; }; + const job_server_action: EnvVarAction = switch (options.job_server_path) { + .unchanged => .nothing, + .deleted => if (map.get("JOBSERVERV2") != null) .delete else .nothing, + .updated => if (map.get("JOBSERVERV2") != null) .edit else .add, + }; const envp_count: usize = c: { var count: usize = map.count(); @@ -1844,6 +1857,11 @@ pub fn createEnvironFromMap( .delete => count -= 1, .nothing, .edit => {}, } + switch (job_server_action) { + .add => count += 1, + .delete => count -= 1, + .nothing, .edit => {}, + } break :c count; }; @@ -1855,6 +1873,11 @@ pub fn createEnvironFromMap( i += 1; } + if (job_server_action == .add) { + envp_buf[i] = try std.fmt.allocPrintZ(arena, "JOBSERVERV2={s}", .{options.job_server_path.updated}); + i += 1; + } + { var it = map.iterator(); while (it.next()) |pair| { @@ -1871,6 +1894,19 @@ pub fn createEnvironFromMap( .nothing => {}, }; + if (mem.eql(u8, pair.key_ptr.*, "JOBSERVERV2")) switch (job_server_action) { + .add => unreachable, + .delete => continue, + .edit => { + envp_buf[i] = try std.fmt.allocPrintZ(arena, "{s}={s}", .{ + pair.key_ptr.*, options.job_server_path.updated, + }); + i += 1; + continue; + }, + .nothing => {}, + }; + envp_buf[i] = try std.fmt.allocPrintZ(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }); i += 1; } @@ -1887,16 +1923,19 @@ pub fn createEnvironFromExisting( existing: [*:null]const ?[*:0]const u8, options: CreateEnvironOptions, ) Allocator.Error![:null]?[*:0]u8 { - const existing_count, const contains_zig_progress = c: { + const existing_count, const contains_zig_progress, const contains_job_server = c: { var count: usize = 0; - var contains = false; + var contains_zig_progress = false; + var contains_job_server = false; while (existing[count]) |line| : (count += 1) { - contains = contains or mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS"); + const name = mem.sliceTo(line, '='); + contains_zig_progress = contains_zig_progress or mem.eql(u8, name, "ZIG_PROGRESS"); + contains_job_server = contains_job_server or mem.eql(u8, name, "JOBSERVERV2"); } - break :c .{ count, contains }; + break :c .{ count, contains_zig_progress, contains_job_server }; }; - const ZigProgressAction = enum { nothing, edit, delete, add }; - const zig_progress_action: ZigProgressAction = a: { + const EnvVarAction = enum { nothing, edit, delete, add }; + const zig_progress_action: EnvVarAction = a: { const fd = options.zig_progress_fd orelse break :a .nothing; if (fd >= 0) { break :a if (contains_zig_progress) .edit else .add; @@ -1905,6 +1944,11 @@ pub fn createEnvironFromExisting( } break :a .nothing; }; + const job_server_action: EnvVarAction = switch (options.job_server_path) { + .unchanged => .nothing, + .deleted => if (contains_job_server) .delete else .nothing, + .updated => if (contains_job_server) .edit else .add, + }; const envp_count: usize = c: { var count: usize = existing_count; @@ -1913,6 +1957,11 @@ pub fn createEnvironFromExisting( .delete => count -= 1, .nothing, .edit => {}, } + switch (job_server_action) { + .add => count += 1, + .delete => count -= 1, + .nothing, .edit => {}, + } break :c count; }; @@ -1924,6 +1973,10 @@ pub fn createEnvironFromExisting( envp_buf[i] = try std.fmt.allocPrintZ(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}); i += 1; } + if (job_server_action == .add) { + envp_buf[i] = try std.fmt.allocPrintZ(arena, "JOBSERVERV2={s}", .{options.job_server_path.updated}); + i += 1; + } while (existing[existing_index]) |line| : (existing_index += 1) { if (mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS")) switch (zig_progress_action) { @@ -1936,6 +1989,16 @@ pub fn createEnvironFromExisting( }, .nothing => {}, }; + if (mem.eql(u8, mem.sliceTo(line, '='), "JOBSERVERV2")) switch (job_server_action) { + .add => unreachable, + .delete => continue, + .edit => { + envp_buf[i] = try std.fmt.allocPrintZ(arena, "JOBSERVERV2={s}", .{options.job_server_path.updated}); + i += 1; + continue; + }, + .nothing => {}, + }; envp_buf[i] = try arena.dupeZ(u8, mem.span(line)); i += 1; } diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index 2f8679420821b7e48d5123297de60fdf16b0db47..36bc255a56a03b2aef755b044e4e8c3ab2a75644 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -103,6 +103,25 @@ resource_usage_statistics: ResourceUsageStatistics = .{}, /// by substituting this node with the child's root node. progress_node: std.Progress.Node = std.Progress.Node.none, +/// When provided, ensures that the child process will have access to the +/// jobserver provided by the thread pool. +/// +/// If the thread pool represents the root process, the child process will be +/// supplied with the `JOBSERVERV2` environment variable so that it can +/// connect. +/// +/// If the thread pool represents a client, its connection address will be +/// passed into the `JOBSERVERV2` environment variable. This potentially +/// overrides the global environment variable. +/// +/// If the thread pool is in abstinance mode, any `JOBSERVERV2` environment +/// variable will be elided from being passed down to the child. This differs +/// from leaving the field as `null` in which case no modifications to +/// jobserver environment variables will occur. +/// +/// A provided thread pool must live longer than this `Child` instance. +thread_pool: ?*std.Thread.Pool = null, + pub const ResourceUsageStatistics = struct { rusage: @TypeOf(rusage_init) = rusage_init, @@ -377,6 +396,7 @@ pub fn run(args: struct { max_output_bytes: usize = 50 * 1024, expand_arg0: Arg0Expand = .no_expand, progress_node: std.Progress.Node = std.Progress.Node.none, + thread_pool: ?*std.Thread.Pool = null, }) RunError!RunResult { var child = ChildProcess.init(args.argv, args.allocator); child.stdin_behavior = .Ignore; @@ -387,6 +407,7 @@ pub fn run(args: struct { child.env_map = args.env_map; child.expand_arg0 = args.expand_arg0; child.progress_node = args.progress_node; + child.thread_pool = args.thread_pool; var stdout = std.ArrayList(u8).init(args.allocator); var stderr = std.ArrayList(u8).init(args.allocator); @@ -616,19 +637,26 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void { const envp: [*:null]const ?[*:0]const u8 = m: { const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno; + const job_server_path: process.CreateEnvironOptions.String = if (self.thread_pool) |thread_pool| switch (thread_pool.job_server_options) { + .host, .connect => |addr| .{ .updated = mem.sliceTo(&addr.un.path, 0) }, + .abstain => .deleted, + } else .unchanged; if (self.env_map) |env_map| { break :m (try process.createEnvironFromMap(arena, env_map, .{ .zig_progress_fd = prog_fd, + .job_server_path = job_server_path, })).ptr; } else if (builtin.link_libc) { break :m (try process.createEnvironFromExisting(arena, std.c.environ, .{ .zig_progress_fd = prog_fd, + .job_server_path = job_server_path, })).ptr; } else if (builtin.output_mode == .Exe) { // Then we have Zig start code and this works. // TODO type-safety for null-termination of `os.environ`. break :m (try process.createEnvironFromExisting(arena, @ptrCast(std.os.environ.ptr), .{ .zig_progress_fd = prog_fd, + .job_server_path = job_server_path, })).ptr; } else { // TODO come up with a solution for this. diff --git a/lib/std/zig.zig b/lib/std/zig.zig index b513c305eebd0dde1b988d1f2ab83543a2f2f712..49e82d922dca2f8bada0b464d6a74ebccc1a57d9 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -689,7 +689,7 @@ pub const EnvVar = enum { CLICOLOR_FORCE, XDG_CACHE_HOME, HOME, - JOBSERVER2, + JOBSERVERV2, pub fn isSet(comptime ev: EnvVar) bool { return std.process.hasEnvVarConstant(@tagName(ev)); @@ -710,15 +710,17 @@ pub const EnvVar = enum { }; pub const ThreadPoolOptions = struct { + allocator: Allocator, n_jobs: ?u32 = null, cache_directory: std.Build.Cache.Directory, }; pub const cache_tmp_basename = "tmp"; -pub fn initThreadPool(gpa: Allocator, options: ThreadPoolOptions) !std.Thread.Pool { - if (EnvVar.JOBSERVER2.getPosix()) |addr_string| { - return std.Thread.Pool.init(gpa, .{ +pub fn initThreadPool(thread_pool: *std.Thread.Pool, options: ThreadPoolOptions) !void { + if (EnvVar.JOBSERVERV2.getPosix()) |addr_string| { + return std.Thread.Pool.init(thread_pool, .{ + .allocator = options.allocator, .n_jobs = options.n_jobs, .job_server = .{ .connect = try std.net.Address.initUnix(addr_string) }, }); @@ -744,13 +746,15 @@ pub fn initThreadPool(gpa: Allocator, options: ThreadPoolOptions) !std.Thread.Po @memcpy(addr.un.path[0..cache_dir.len], cache_dir); @memcpy(addr.un.path[cache_dir.len..][0..suffix.len], suffix); - return std.Thread.Pool.init(gpa, .{ + return std.Thread.Pool.init(thread_pool, .{ + .allocator = options.allocator, .n_jobs = options.n_jobs, .job_server = .{ .host = addr }, }) catch |err| switch (err) { error.FileNotFound => { try options.cache_directory.handle.makePath(cache_tmp_basename); - return std.Thread.Pool.init(gpa, .{ + return std.Thread.Pool.init(thread_pool, .{ + .allocator = options.allocator, .n_jobs = options.n_jobs, .job_server = .{ .host = addr }, }); diff --git a/src/Compilation.zig b/src/Compilation.zig index b30f65ad114631e82f4e6e6ea2af738e6c063c28..8fbd281d512daa83eb4da73d80c05467893a2c0e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4604,6 +4604,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr }; if (std.process.can_spawn) { var child = std.process.Child.init(argv.items, arena); + child.thread_pool = comp.thread_pool; if (comp.clang_passthrough_mode) { child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; @@ -4964,6 +4965,7 @@ fn spawnZigRc( child.stdout_behavior = .Pipe; child.stderr_behavior = .Pipe; child.progress_node = child_progress_node; + child.thread_pool = comp.thread_pool; child.spawn() catch |err| { return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) }); diff --git a/src/link.zig b/src/link.zig index 75a9723f1c044b921e142f6276ecfef3f8e57c74..dc4b55bf385b1470ebdae048c635f722f96a7b61 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1011,6 +1011,7 @@ pub fn spawnLld( defer comp.gpa.free(stderr); var child = std.process.Child.init(argv, arena); + child.thread_pool = comp.thread_pool; const term = (if (comp.clang_passthrough_mode) term: { child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; diff --git a/src/main.zig b/src/main.zig index 8b506366a3ea26f76c6059acbc8aa20ad685d0b0..11377937e67f92844eea8086f632eedcb8554512 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3136,7 +3136,9 @@ fn buildOutputType( break :l global_cache_directory; }; - var thread_pool = try std.zig.initThreadPool(gpa, .{ + var thread_pool: std.Thread.Pool = undefined; + try std.zig.initThreadPool(&thread_pool, .{ + .allocator = gpa, .cache_directory = local_cache_directory, }); defer thread_pool.deinit(); @@ -4250,6 +4252,7 @@ fn runOrTest( child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; child.stderr_behavior = .Inherit; + child.thread_pool = comp.thread_pool; // Here we release all the locks associated with the Compilation so // that whatever this child process wants to do won't deadlock. @@ -4395,6 +4398,7 @@ fn runOrTestHotSwap( child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; child.stderr_behavior = .Inherit; + child.thread_pool = comp.thread_pool; try child.spawn(); @@ -4896,7 +4900,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { child_argv.items[argv_index_cache_dir] = local_cache_directory.path orelse cwd_path; - var thread_pool = try std.zig.initThreadPool(gpa, .{ + var thread_pool: std.Thread.Pool = undefined; + try std.zig.initThreadPool(&thread_pool, .{ + .allocator = gpa, .cache_directory = local_cache_directory, }); defer thread_pool.deinit(); @@ -5185,6 +5191,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; child.stderr_behavior = .Inherit; + child.thread_pool = &thread_pool; const term = t: { std.debug.lockStdErr(); @@ -5331,7 +5338,9 @@ fn jitCmd( }; defer global_cache_directory.handle.close(); - var thread_pool = try std.zig.initThreadPool(gpa, .{ + var thread_pool: std.Thread.Pool = undefined; + try std.zig.initThreadPool(&thread_pool, .{ + .allocator = gpa, .cache_directory = global_cache_directory, }); defer thread_pool.deinit(); @@ -5474,6 +5483,7 @@ fn jitCmd( child.stdin_behavior = .Inherit; child.stdout_behavior = if (options.capture == null) .Inherit else .Pipe; child.stderr_behavior = .Inherit; + child.thread_pool = &thread_pool; try child.spawn(); @@ -6897,7 +6907,9 @@ fn cmdFetch( }; defer global_cache_directory.handle.close(); - var thread_pool = try std.zig.initThreadPool(gpa, .{ + var thread_pool: std.Thread.Pool = undefined; + try std.zig.initThreadPool(&thread_pool, .{ + .allocator = gpa, .cache_directory = global_cache_directory, }); defer thread_pool.deinit(); -- 2.54.0