| author | |
| committer | |
| log | e51fd6728fcc4dd93e3bd1ddb2e4ca96a8082b12 |
| tree | 07b916bb71e2f6f733575bb3396a52c2a58a6af0 |
| parent | c395df25aba0f1bdc1dd0cb6b9c7f14a90e72dae |
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.11 files changed, 171 insertions(+), 85 deletions(-)
lib/compiler/build_runner.zig+9-5| ... | ... | @@ -74,6 +74,7 @@ pub fn main() !void { |
| 74 | 74 | .query = .{}, |
| 75 | 75 | .result = try std.zig.system.resolveTargetQuery(.{}), |
| 76 | 76 | }, |
| 77 | .thread_pool = undefined, | |
| 77 | 78 | }; |
| 78 | 79 | |
| 79 | 80 | graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); |
| ... | ... | @@ -92,6 +93,7 @@ pub fn main() !void { |
| 92 | 93 | var targets = ArrayList([]const u8).init(arena); |
| 93 | 94 | var debug_log_scopes = ArrayList([]const u8).init(arena); |
| 94 | 95 | var thread_pool_options: std.zig.ThreadPoolOptions = .{ |
| 96 | .allocator = arena, | |
| 95 | 97 | .cache_directory = local_cache_directory, |
| 96 | 98 | }; |
| 97 | 99 | |
| ... | ... | @@ -448,7 +450,8 @@ fn runStepNames( |
| 448 | 450 | } |
| 449 | 451 | } |
| 450 | 452 | |
| 451 | var thread_pool = try std.zig.initThreadPool(gpa, thread_pool_options); | |
| 453 | const thread_pool = &b.graph.thread_pool; | |
| 454 | try std.zig.initThreadPool(thread_pool, thread_pool_options); | |
| 452 | 455 | defer thread_pool.deinit(); |
| 453 | 456 | |
| 454 | 457 | { |
| ... | ... | @@ -469,7 +472,7 @@ fn runStepNames( |
| 469 | 472 | if (step.state == .skipped_oom) continue; |
| 470 | 473 | |
| 471 | 474 | thread_pool.spawnWg(&wait_group, workerMakeOneStep, .{ |
| 472 | &wait_group, &thread_pool, b, step, step_prog, run, | |
| 475 | &wait_group, b, step, step_prog, run, | |
| 473 | 476 | }); |
| 474 | 477 | } |
| 475 | 478 | } |
| ... | ... | @@ -890,12 +893,13 @@ fn constructGraphAndCheckForDependencyLoop( |
| 890 | 893 | |
| 891 | 894 | fn workerMakeOneStep( |
| 892 | 895 | wg: *std.Thread.WaitGroup, |
| 893 | thread_pool: *std.Thread.Pool, | |
| 894 | 896 | b: *std.Build, |
| 895 | 897 | s: *Step, |
| 896 | 898 | prog_node: std.Progress.Node, |
| 897 | 899 | run: *Run, |
| 898 | 900 | ) void { |
| 901 | const thread_pool = &b.graph.thread_pool; | |
| 902 | ||
| 899 | 903 | // First, check the conditions for running this step. If they are not met, |
| 900 | 904 | // then we return without doing the step, relying on another worker to |
| 901 | 905 | // queue this step up again when dependencies are met. |
| ... | ... | @@ -975,7 +979,7 @@ fn workerMakeOneStep( |
| 975 | 979 | // Successful completion of a step, so we queue up its dependants as well. |
| 976 | 980 | for (s.dependants.items) |dep| { |
| 977 | 981 | thread_pool.spawnWg(wg, workerMakeOneStep, .{ |
| 978 | wg, thread_pool, b, dep, prog_node, run, | |
| 982 | wg, b, dep, prog_node, run, | |
| 979 | 983 | }); |
| 980 | 984 | } |
| 981 | 985 | } |
| ... | ... | @@ -1000,7 +1004,7 @@ fn workerMakeOneStep( |
| 1000 | 1004 | remaining -= dep.max_rss; |
| 1001 | 1005 | |
| 1002 | 1006 | thread_pool.spawnWg(wg, workerMakeOneStep, .{ |
| 1003 | wg, thread_pool, b, dep, prog_node, run, | |
| 1007 | wg, b, dep, prog_node, run, | |
| 1004 | 1008 | }); |
| 1005 | 1009 | } else { |
| 1006 | 1010 | run.memory_blocked_steps.items[i] = dep; |
lib/std/Build.zig+2| ... | ... | @@ -120,6 +120,8 @@ pub const Graph = struct { |
| 120 | 120 | needed_lazy_dependencies: std.StringArrayHashMapUnmanaged(void) = .{}, |
| 121 | 121 | /// Information about the native target. Computed before build() is invoked. |
| 122 | 122 | host: ResolvedTarget, |
| 123 | /// Uninitialized until the make phase. | |
| 124 | thread_pool: std.Thread.Pool, | |
| 123 | 125 | }; |
| 124 | 126 | |
| 125 | 127 | const AvailableDeps = []const struct { []const u8, []const u8 }; |
lib/std/Build/Step.zig+5-2| ... | ... | @@ -283,15 +283,17 @@ pub fn captureChildProcess( |
| 283 | 283 | progress_node: std.Progress.Node, |
| 284 | 284 | argv: []const []const u8, |
| 285 | 285 | ) !std.process.Child.RunResult { |
| 286 | const arena = s.owner.allocator; | |
| 286 | const b = s.owner; | |
| 287 | const arena = b.allocator; | |
| 287 | 288 | |
| 288 | 289 | try handleChildProcUnsupported(s, null, argv); |
| 289 | try handleVerbose(s.owner, null, argv); | |
| 290 | try handleVerbose(b, null, argv); | |
| 290 | 291 | |
| 291 | 292 | const result = std.process.Child.run(.{ |
| 292 | 293 | .allocator = arena, |
| 293 | 294 | .argv = argv, |
| 294 | 295 | .progress_node = progress_node, |
| 296 | .thread_pool = &b.graph.thread_pool, | |
| 295 | 297 | }) catch |err| return s.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); |
| 296 | 298 | |
| 297 | 299 | if (result.stderr.len > 0) { |
| ... | ... | @@ -334,6 +336,7 @@ pub fn evalZigProcess( |
| 334 | 336 | child.stderr_behavior = .Pipe; |
| 335 | 337 | child.request_resource_usage_statistics = true; |
| 336 | 338 | child.progress_node = prog_node; |
| 339 | child.thread_pool = &b.graph.thread_pool; | |
| 337 | 340 | |
| 338 | 341 | child.spawn() catch |err| return s.fail("unable to spawn {s}: {s}", .{ |
| 339 | 342 | argv[0], @errorName(err), |
lib/std/Build/Step/Run.zig+1| ... | ... | @@ -1246,6 +1246,7 @@ fn spawnChildAndCollect( |
| 1246 | 1246 | if (run.stdio != .zig_test and !run.disable_zig_progress and !inherit) { |
| 1247 | 1247 | child.progress_node = prog_node; |
| 1248 | 1248 | } |
| 1249 | child.thread_pool = &b.graph.thread_pool; | |
| 1249 | 1250 | |
| 1250 | 1251 | const term, const result, const elapsed_ns = t: { |
| 1251 | 1252 | if (inherit) std.debug.lockStdErr(); |
lib/std/Thread/Pool.zig+26-60| ... | ... | @@ -7,11 +7,9 @@ const assert = std.debug.assert; |
| 7 | 7 | mutex: std.Thread.Mutex, |
| 8 | 8 | cond: std.Thread.Condition, |
| 9 | 9 | run_queue: RunQueue, |
| 10 | run_queue_len: usize, | |
| 11 | 10 | end_flag: bool, |
| 12 | 11 | allocator: std.mem.Allocator, |
| 13 | threads_buffer: []std.Thread, | |
| 14 | threads_len: usize, | |
| 12 | threads: []std.Thread, | |
| 15 | 13 | job_server_options: Options.JobServer, |
| 16 | 14 | job_server: ?*JobServer, |
| 17 | 15 | |
| ... | ... | @@ -23,6 +21,9 @@ const Runnable = struct { |
| 23 | 21 | const RunProto = *const fn (*Runnable) void; |
| 24 | 22 | |
| 25 | 23 | pub const Options = struct { |
| 24 | /// Not required to be thread-safe; protected by the pool's mutex. | |
| 25 | allocator: std.mem.Allocator, | |
| 26 | ||
| 26 | 27 | /// Max number of threads to be actively working at the same time. |
| 27 | 28 | /// |
| 28 | 29 | /// `null` means to use the logical core count, leaving the main thread to |
| ... | ... | @@ -49,22 +50,16 @@ pub const Options = struct { |
| 49 | 50 | }; |
| 50 | 51 | }; |
| 51 | 52 | |
| 52 | /// After initializing the thread pool and spawning work, the main thread must | |
| 53 | /// call `waitAndWork`. | |
| 54 | pub fn init( | |
| 55 | /// Not required to be thread-safe; protected by the pool's mutex. | |
| 56 | allocator: std.mem.Allocator, | |
| 57 | options: Options, | |
| 58 | ) !Pool { | |
| 59 | var pool: Pool = .{ | |
| 53 | pub fn init(pool: *Pool, options: Options) !void { | |
| 54 | const allocator = options.allocator; | |
| 55 | ||
| 56 | pool.* = .{ | |
| 60 | 57 | .mutex = .{}, |
| 61 | 58 | .cond = .{}, |
| 62 | 59 | .run_queue = .{}, |
| 63 | .run_queue_len = 0, | |
| 64 | 60 | .end_flag = false, |
| 65 | 61 | .allocator = allocator, |
| 66 | .threads_buffer = &.{}, | |
| 67 | .threads_len = 0, | |
| 62 | .threads = &.{}, | |
| 68 | 63 | .job_server_options = options.job_server, |
| 69 | 64 | .job_server = null, |
| 70 | 65 | }; |
| ... | ... | @@ -75,8 +70,15 @@ pub fn init( |
| 75 | 70 | const thread_count = options.n_jobs orelse @max(1, std.Thread.getCpuCount() catch 1); |
| 76 | 71 | assert(thread_count > 0); |
| 77 | 72 | |
| 78 | pool.threads_buffer = try allocator.alloc(std.Thread, thread_count); | |
| 79 | errdefer allocator.free(pool.threads_buffer); | |
| 73 | // Kill and join any threads we spawned and free memory on error. | |
| 74 | pool.threads = try allocator.alloc(std.Thread, thread_count); | |
| 75 | var spawned: usize = 0; | |
| 76 | errdefer pool.join(spawned); | |
| 77 | ||
| 78 | for (pool.threads) |*thread| { | |
| 79 | thread.* = try std.Thread.spawn(.{}, worker, .{pool}); | |
| 80 | spawned += 1; | |
| 81 | } | |
| 80 | 82 | |
| 81 | 83 | switch (options.job_server) { |
| 82 | 84 | .abstain, .connect => {}, |
| ... | ... | @@ -84,7 +86,7 @@ pub fn init( |
| 84 | 86 | var server = try addr.listen(.{}); |
| 85 | 87 | errdefer server.deinit(); |
| 86 | 88 | |
| 87 | const pollfds = try allocator.alloc(std.posix.pollfd, thread_count); | |
| 89 | const pollfds = try allocator.alloc(std.posix.pollfd, thread_count + 1); | |
| 88 | 90 | errdefer allocator.free(pollfds); |
| 89 | 91 | |
| 90 | 92 | const job_server = try allocator.create(JobServer); |
| ... | ... | @@ -99,11 +101,14 @@ pub fn init( |
| 99 | 101 | pool.job_server = job_server; |
| 100 | 102 | }, |
| 101 | 103 | } |
| 102 | ||
| 103 | return pool; | |
| 104 | 104 | } |
| 105 | 105 | |
| 106 | 106 | pub fn deinit(pool: *Pool) void { |
| 107 | pool.join(pool.threads.len); | |
| 108 | pool.* = undefined; | |
| 109 | } | |
| 110 | ||
| 111 | fn join(pool: *Pool, spawned: usize) void { | |
| 107 | 112 | if (builtin.single_threaded) |
| 108 | 113 | return; |
| 109 | 114 | |
| ... | ... | @@ -127,15 +132,10 @@ pub fn deinit(pool: *Pool) void { |
| 127 | 132 | job_server.thread.join(); |
| 128 | 133 | } |
| 129 | 134 | |
| 130 | // Since we set end_flag with the mutex locked, no more threads could have | |
| 131 | // been created. | |
| 132 | const threads = pool.threads_buffer[0..pool.threads_len]; | |
| 133 | ||
| 134 | for (threads) |thread| | |
| 135 | for (pool.threads[0..spawned]) |thread| | |
| 135 | 136 | thread.join(); |
| 136 | 137 | |
| 137 | pool.allocator.free(pool.threads_buffer); | |
| 138 | pool.* = undefined; | |
| 138 | pool.allocator.free(pool.threads); | |
| 139 | 139 | } |
| 140 | 140 | |
| 141 | 141 | pub const JobServer = struct { |
| ... | ... | @@ -256,22 +256,6 @@ pub fn spawnWg(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, args |
| 256 | 256 | }; |
| 257 | 257 | |
| 258 | 258 | pool.run_queue.prepend(&closure.run_node); |
| 259 | pool.run_queue_len += 1; | |
| 260 | ||
| 261 | // If there was already any queued work, spawn a new thread if we are | |
| 262 | // under the max. | |
| 263 | if (pool.run_queue_len > 1 and pool.threads_len < pool.threads_buffer.len) { | |
| 264 | if (std.Thread.spawn(.{}, worker, .{pool})) |new_thread| { | |
| 265 | pool.threads_buffer[pool.threads_len] = new_thread; | |
| 266 | pool.threads_len += 1; | |
| 267 | } else |_| if (pool.threads_len == 0) { | |
| 268 | pool.mutex.unlock(); | |
| 269 | @call(.auto, func, args); | |
| 270 | wait_group.finish(); | |
| 271 | return; | |
| 272 | } | |
| 273 | } | |
| 274 | ||
| 275 | 259 | pool.mutex.unlock(); |
| 276 | 260 | } |
| 277 | 261 | |
| ... | ... | @@ -319,21 +303,6 @@ pub fn spawn(pool: *Pool, comptime func: anytype, args: anytype) void { |
| 319 | 303 | }; |
| 320 | 304 | |
| 321 | 305 | pool.run_queue.prepend(&closure.run_node); |
| 322 | pool.run_queue_len += 1; | |
| 323 | ||
| 324 | // If there was already any queued work, spawn a new thread if we are | |
| 325 | // under the max. | |
| 326 | if (pool.run_queue_len > 1 and pool.threads_len < pool.threads_buffer.len) { | |
| 327 | if (std.Thread.spawn(.{}, worker, .{pool})) |new_thread| { | |
| 328 | pool.threads_buffer[pool.threads_len] = new_thread; | |
| 329 | pool.threads_len += 1; | |
| 330 | } else |_| if (pool.threads_len == 0) { | |
| 331 | pool.mutex.unlock(); | |
| 332 | @call(.auto, func, args); | |
| 333 | return; | |
| 334 | } | |
| 335 | } | |
| 336 | ||
| 337 | 306 | pool.mutex.unlock(); |
| 338 | 307 | } |
| 339 | 308 | |
| ... | ... | @@ -351,8 +320,6 @@ fn worker(pool: *Pool) void { |
| 351 | 320 | |
| 352 | 321 | while (true) { |
| 353 | 322 | while (pool.run_queue.popFirst()) |run_node| { |
| 354 | pool.run_queue_len -= 1; | |
| 355 | ||
| 356 | 323 | // Temporarily unlock the mutex in order to execute the run_node. |
| 357 | 324 | pool.mutex.unlock(); |
| 358 | 325 | defer pool.mutex.lock(); |
| ... | ... | @@ -391,7 +358,6 @@ pub fn waitAndWork(pool: *Pool, wait_group: *WaitGroup) void { |
| 391 | 358 | defer pool.mutex.unlock(); |
| 392 | 359 | break :blk pool.run_queue.popFirst(); |
| 393 | 360 | }) |run_node| { |
| 394 | pool.run_queue_len -= 1; | |
| 395 | 361 | run_node.data.runFn(&run_node.data); |
| 396 | 362 | continue; |
| 397 | 363 | } |
lib/std/process.zig+71-8| ... | ... | @@ -1816,6 +1816,14 @@ pub const CreateEnvironOptions = struct { |
| 1816 | 1816 | /// If non-null, negative means to remove the environment variable, and >= 0 |
| 1817 | 1817 | /// means to provide it with the given integer. |
| 1818 | 1818 | zig_progress_fd: ?i32 = null, |
| 1819 | ||
| 1820 | job_server_path: String = .unchanged, | |
| 1821 | ||
| 1822 | pub const String = union(enum) { | |
| 1823 | unchanged, | |
| 1824 | deleted, | |
| 1825 | updated: []const u8, | |
| 1826 | }; | |
| 1819 | 1827 | }; |
| 1820 | 1828 | |
| 1821 | 1829 | /// Creates a null-deliminated environment variable block in the format |
| ... | ... | @@ -1825,8 +1833,8 @@ pub fn createEnvironFromMap( |
| 1825 | 1833 | map: *const EnvMap, |
| 1826 | 1834 | options: CreateEnvironOptions, |
| 1827 | 1835 | ) Allocator.Error![:null]?[*:0]u8 { |
| 1828 | const ZigProgressAction = enum { nothing, edit, delete, add }; | |
| 1829 | const zig_progress_action: ZigProgressAction = a: { | |
| 1836 | const EnvVarAction = enum { nothing, edit, delete, add }; | |
| 1837 | const zig_progress_action: EnvVarAction = a: { | |
| 1830 | 1838 | const fd = options.zig_progress_fd orelse break :a .nothing; |
| 1831 | 1839 | const contains = map.get("ZIG_PROGRESS") != null; |
| 1832 | 1840 | if (fd >= 0) { |
| ... | ... | @@ -1836,6 +1844,11 @@ pub fn createEnvironFromMap( |
| 1836 | 1844 | } |
| 1837 | 1845 | break :a .nothing; |
| 1838 | 1846 | }; |
| 1847 | const job_server_action: EnvVarAction = switch (options.job_server_path) { | |
| 1848 | .unchanged => .nothing, | |
| 1849 | .deleted => if (map.get("JOBSERVERV2") != null) .delete else .nothing, | |
| 1850 | .updated => if (map.get("JOBSERVERV2") != null) .edit else .add, | |
| 1851 | }; | |
| 1839 | 1852 | |
| 1840 | 1853 | const envp_count: usize = c: { |
| 1841 | 1854 | var count: usize = map.count(); |
| ... | ... | @@ -1844,6 +1857,11 @@ pub fn createEnvironFromMap( |
| 1844 | 1857 | .delete => count -= 1, |
| 1845 | 1858 | .nothing, .edit => {}, |
| 1846 | 1859 | } |
| 1860 | switch (job_server_action) { | |
| 1861 | .add => count += 1, | |
| 1862 | .delete => count -= 1, | |
| 1863 | .nothing, .edit => {}, | |
| 1864 | } | |
| 1847 | 1865 | break :c count; |
| 1848 | 1866 | }; |
| 1849 | 1867 | |
| ... | ... | @@ -1855,6 +1873,11 @@ pub fn createEnvironFromMap( |
| 1855 | 1873 | i += 1; |
| 1856 | 1874 | } |
| 1857 | 1875 | |
| 1876 | if (job_server_action == .add) { | |
| 1877 | envp_buf[i] = try std.fmt.allocPrintZ(arena, "JOBSERVERV2={s}", .{options.job_server_path.updated}); | |
| 1878 | i += 1; | |
| 1879 | } | |
| 1880 | ||
| 1858 | 1881 | { |
| 1859 | 1882 | var it = map.iterator(); |
| 1860 | 1883 | while (it.next()) |pair| { |
| ... | ... | @@ -1871,6 +1894,19 @@ pub fn createEnvironFromMap( |
| 1871 | 1894 | .nothing => {}, |
| 1872 | 1895 | }; |
| 1873 | 1896 | |
| 1897 | if (mem.eql(u8, pair.key_ptr.*, "JOBSERVERV2")) switch (job_server_action) { | |
| 1898 | .add => unreachable, | |
| 1899 | .delete => continue, | |
| 1900 | .edit => { | |
| 1901 | envp_buf[i] = try std.fmt.allocPrintZ(arena, "{s}={s}", .{ | |
| 1902 | pair.key_ptr.*, options.job_server_path.updated, | |
| 1903 | }); | |
| 1904 | i += 1; | |
| 1905 | continue; | |
| 1906 | }, | |
| 1907 | .nothing => {}, | |
| 1908 | }; | |
| 1909 | ||
| 1874 | 1910 | envp_buf[i] = try std.fmt.allocPrintZ(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }); |
| 1875 | 1911 | i += 1; |
| 1876 | 1912 | } |
| ... | ... | @@ -1887,16 +1923,19 @@ pub fn createEnvironFromExisting( |
| 1887 | 1923 | existing: [*:null]const ?[*:0]const u8, |
| 1888 | 1924 | options: CreateEnvironOptions, |
| 1889 | 1925 | ) Allocator.Error![:null]?[*:0]u8 { |
| 1890 | const existing_count, const contains_zig_progress = c: { | |
| 1926 | const existing_count, const contains_zig_progress, const contains_job_server = c: { | |
| 1891 | 1927 | var count: usize = 0; |
| 1892 | var contains = false; | |
| 1928 | var contains_zig_progress = false; | |
| 1929 | var contains_job_server = false; | |
| 1893 | 1930 | while (existing[count]) |line| : (count += 1) { |
| 1894 | contains = contains or mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS"); | |
| 1931 | const name = mem.sliceTo(line, '='); | |
| 1932 | contains_zig_progress = contains_zig_progress or mem.eql(u8, name, "ZIG_PROGRESS"); | |
| 1933 | contains_job_server = contains_job_server or mem.eql(u8, name, "JOBSERVERV2"); | |
| 1895 | 1934 | } |
| 1896 | break :c .{ count, contains }; | |
| 1935 | break :c .{ count, contains_zig_progress, contains_job_server }; | |
| 1897 | 1936 | }; |
| 1898 | const ZigProgressAction = enum { nothing, edit, delete, add }; | |
| 1899 | const zig_progress_action: ZigProgressAction = a: { | |
| 1937 | const EnvVarAction = enum { nothing, edit, delete, add }; | |
| 1938 | const zig_progress_action: EnvVarAction = a: { | |
| 1900 | 1939 | const fd = options.zig_progress_fd orelse break :a .nothing; |
| 1901 | 1940 | if (fd >= 0) { |
| 1902 | 1941 | break :a if (contains_zig_progress) .edit else .add; |
| ... | ... | @@ -1905,6 +1944,11 @@ pub fn createEnvironFromExisting( |
| 1905 | 1944 | } |
| 1906 | 1945 | break :a .nothing; |
| 1907 | 1946 | }; |
| 1947 | const job_server_action: EnvVarAction = switch (options.job_server_path) { | |
| 1948 | .unchanged => .nothing, | |
| 1949 | .deleted => if (contains_job_server) .delete else .nothing, | |
| 1950 | .updated => if (contains_job_server) .edit else .add, | |
| 1951 | }; | |
| 1908 | 1952 | |
| 1909 | 1953 | const envp_count: usize = c: { |
| 1910 | 1954 | var count: usize = existing_count; |
| ... | ... | @@ -1913,6 +1957,11 @@ pub fn createEnvironFromExisting( |
| 1913 | 1957 | .delete => count -= 1, |
| 1914 | 1958 | .nothing, .edit => {}, |
| 1915 | 1959 | } |
| 1960 | switch (job_server_action) { | |
| 1961 | .add => count += 1, | |
| 1962 | .delete => count -= 1, | |
| 1963 | .nothing, .edit => {}, | |
| 1964 | } | |
| 1916 | 1965 | break :c count; |
| 1917 | 1966 | }; |
| 1918 | 1967 | |
| ... | ... | @@ -1924,6 +1973,10 @@ pub fn createEnvironFromExisting( |
| 1924 | 1973 | envp_buf[i] = try std.fmt.allocPrintZ(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}); |
| 1925 | 1974 | i += 1; |
| 1926 | 1975 | } |
| 1976 | if (job_server_action == .add) { | |
| 1977 | envp_buf[i] = try std.fmt.allocPrintZ(arena, "JOBSERVERV2={s}", .{options.job_server_path.updated}); | |
| 1978 | i += 1; | |
| 1979 | } | |
| 1927 | 1980 | |
| 1928 | 1981 | while (existing[existing_index]) |line| : (existing_index += 1) { |
| 1929 | 1982 | if (mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS")) switch (zig_progress_action) { |
| ... | ... | @@ -1936,6 +1989,16 @@ pub fn createEnvironFromExisting( |
| 1936 | 1989 | }, |
| 1937 | 1990 | .nothing => {}, |
| 1938 | 1991 | }; |
| 1992 | if (mem.eql(u8, mem.sliceTo(line, '='), "JOBSERVERV2")) switch (job_server_action) { | |
| 1993 | .add => unreachable, | |
| 1994 | .delete => continue, | |
| 1995 | .edit => { | |
| 1996 | envp_buf[i] = try std.fmt.allocPrintZ(arena, "JOBSERVERV2={s}", .{options.job_server_path.updated}); | |
| 1997 | i += 1; | |
| 1998 | continue; | |
| 1999 | }, | |
| 2000 | .nothing => {}, | |
| 2001 | }; | |
| 1939 | 2002 | envp_buf[i] = try arena.dupeZ(u8, mem.span(line)); |
| 1940 | 2003 | i += 1; |
| 1941 | 2004 | } |
lib/std/process/Child.zig+28| ... | ... | @@ -103,6 +103,25 @@ resource_usage_statistics: ResourceUsageStatistics = .{}, |
| 103 | 103 | /// by substituting this node with the child's root node. |
| 104 | 104 | progress_node: std.Progress.Node = std.Progress.Node.none, |
| 105 | 105 | |
| 106 | /// When provided, ensures that the child process will have access to the | |
| 107 | /// jobserver provided by the thread pool. | |
| 108 | /// | |
| 109 | /// If the thread pool represents the root process, the child process will be | |
| 110 | /// supplied with the `JOBSERVERV2` environment variable so that it can | |
| 111 | /// connect. | |
| 112 | /// | |
| 113 | /// If the thread pool represents a client, its connection address will be | |
| 114 | /// passed into the `JOBSERVERV2` environment variable. This potentially | |
| 115 | /// overrides the global environment variable. | |
| 116 | /// | |
| 117 | /// If the thread pool is in abstinance mode, any `JOBSERVERV2` environment | |
| 118 | /// variable will be elided from being passed down to the child. This differs | |
| 119 | /// from leaving the field as `null` in which case no modifications to | |
| 120 | /// jobserver environment variables will occur. | |
| 121 | /// | |
| 122 | /// A provided thread pool must live longer than this `Child` instance. | |
| 123 | thread_pool: ?*std.Thread.Pool = null, | |
| 124 | ||
| 106 | 125 | pub const ResourceUsageStatistics = struct { |
| 107 | 126 | rusage: @TypeOf(rusage_init) = rusage_init, |
| 108 | 127 | |
| ... | ... | @@ -377,6 +396,7 @@ pub fn run(args: struct { |
| 377 | 396 | max_output_bytes: usize = 50 * 1024, |
| 378 | 397 | expand_arg0: Arg0Expand = .no_expand, |
| 379 | 398 | progress_node: std.Progress.Node = std.Progress.Node.none, |
| 399 | thread_pool: ?*std.Thread.Pool = null, | |
| 380 | 400 | }) RunError!RunResult { |
| 381 | 401 | var child = ChildProcess.init(args.argv, args.allocator); |
| 382 | 402 | child.stdin_behavior = .Ignore; |
| ... | ... | @@ -387,6 +407,7 @@ pub fn run(args: struct { |
| 387 | 407 | child.env_map = args.env_map; |
| 388 | 408 | child.expand_arg0 = args.expand_arg0; |
| 389 | 409 | child.progress_node = args.progress_node; |
| 410 | child.thread_pool = args.thread_pool; | |
| 390 | 411 | |
| 391 | 412 | var stdout = std.ArrayList(u8).init(args.allocator); |
| 392 | 413 | var stderr = std.ArrayList(u8).init(args.allocator); |
| ... | ... | @@ -616,19 +637,26 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void { |
| 616 | 637 | |
| 617 | 638 | const envp: [*:null]const ?[*:0]const u8 = m: { |
| 618 | 639 | const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno; |
| 640 | const job_server_path: process.CreateEnvironOptions.String = if (self.thread_pool) |thread_pool| switch (thread_pool.job_server_options) { | |
| 641 | .host, .connect => |addr| .{ .updated = mem.sliceTo(&addr.un.path, 0) }, | |
| 642 | .abstain => .deleted, | |
| 643 | } else .unchanged; | |
| 619 | 644 | if (self.env_map) |env_map| { |
| 620 | 645 | break :m (try process.createEnvironFromMap(arena, env_map, .{ |
| 621 | 646 | .zig_progress_fd = prog_fd, |
| 647 | .job_server_path = job_server_path, | |
| 622 | 648 | })).ptr; |
| 623 | 649 | } else if (builtin.link_libc) { |
| 624 | 650 | break :m (try process.createEnvironFromExisting(arena, std.c.environ, .{ |
| 625 | 651 | .zig_progress_fd = prog_fd, |
| 652 | .job_server_path = job_server_path, | |
| 626 | 653 | })).ptr; |
| 627 | 654 | } else if (builtin.output_mode == .Exe) { |
| 628 | 655 | // Then we have Zig start code and this works. |
| 629 | 656 | // TODO type-safety for null-termination of `os.environ`. |
| 630 | 657 | break :m (try process.createEnvironFromExisting(arena, @ptrCast(std.os.environ.ptr), .{ |
| 631 | 658 | .zig_progress_fd = prog_fd, |
| 659 | .job_server_path = job_server_path, | |
| 632 | 660 | })).ptr; |
| 633 | 661 | } else { |
| 634 | 662 | // TODO come up with a solution for this. |
lib/std/zig.zig+10-6| ... | ... | @@ -689,7 +689,7 @@ pub const EnvVar = enum { |
| 689 | 689 | CLICOLOR_FORCE, |
| 690 | 690 | XDG_CACHE_HOME, |
| 691 | 691 | HOME, |
| 692 | JOBSERVER2, | |
| 692 | JOBSERVERV2, | |
| 693 | 693 | |
| 694 | 694 | pub fn isSet(comptime ev: EnvVar) bool { |
| 695 | 695 | return std.process.hasEnvVarConstant(@tagName(ev)); |
| ... | ... | @@ -710,15 +710,17 @@ pub const EnvVar = enum { |
| 710 | 710 | }; |
| 711 | 711 | |
| 712 | 712 | pub const ThreadPoolOptions = struct { |
| 713 | allocator: Allocator, | |
| 713 | 714 | n_jobs: ?u32 = null, |
| 714 | 715 | cache_directory: std.Build.Cache.Directory, |
| 715 | 716 | }; |
| 716 | 717 | |
| 717 | 718 | pub const cache_tmp_basename = "tmp"; |
| 718 | 719 | |
| 719 | pub fn initThreadPool(gpa: Allocator, options: ThreadPoolOptions) !std.Thread.Pool { | |
| 720 | if (EnvVar.JOBSERVER2.getPosix()) |addr_string| { | |
| 721 | return std.Thread.Pool.init(gpa, .{ | |
| 720 | pub fn initThreadPool(thread_pool: *std.Thread.Pool, options: ThreadPoolOptions) !void { | |
| 721 | if (EnvVar.JOBSERVERV2.getPosix()) |addr_string| { | |
| 722 | return std.Thread.Pool.init(thread_pool, .{ | |
| 723 | .allocator = options.allocator, | |
| 722 | 724 | .n_jobs = options.n_jobs, |
| 723 | 725 | .job_server = .{ .connect = try std.net.Address.initUnix(addr_string) }, |
| 724 | 726 | }); |
| ... | ... | @@ -744,13 +746,15 @@ pub fn initThreadPool(gpa: Allocator, options: ThreadPoolOptions) !std.Thread.Po |
| 744 | 746 | @memcpy(addr.un.path[0..cache_dir.len], cache_dir); |
| 745 | 747 | @memcpy(addr.un.path[cache_dir.len..][0..suffix.len], suffix); |
| 746 | 748 | |
| 747 | return std.Thread.Pool.init(gpa, .{ | |
| 749 | return std.Thread.Pool.init(thread_pool, .{ | |
| 750 | .allocator = options.allocator, | |
| 748 | 751 | .n_jobs = options.n_jobs, |
| 749 | 752 | .job_server = .{ .host = addr }, |
| 750 | 753 | }) catch |err| switch (err) { |
| 751 | 754 | error.FileNotFound => { |
| 752 | 755 | try options.cache_directory.handle.makePath(cache_tmp_basename); |
| 753 | return std.Thread.Pool.init(gpa, .{ | |
| 756 | return std.Thread.Pool.init(thread_pool, .{ | |
| 757 | .allocator = options.allocator, | |
| 754 | 758 | .n_jobs = options.n_jobs, |
| 755 | 759 | .job_server = .{ .host = addr }, |
| 756 | 760 | }); |
src/Compilation.zig+2| ... | ... | @@ -4604,6 +4604,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr |
| 4604 | 4604 | }; |
| 4605 | 4605 | if (std.process.can_spawn) { |
| 4606 | 4606 | var child = std.process.Child.init(argv.items, arena); |
| 4607 | child.thread_pool = comp.thread_pool; | |
| 4607 | 4608 | if (comp.clang_passthrough_mode) { |
| 4608 | 4609 | child.stdin_behavior = .Inherit; |
| 4609 | 4610 | child.stdout_behavior = .Inherit; |
| ... | ... | @@ -4964,6 +4965,7 @@ fn spawnZigRc( |
| 4964 | 4965 | child.stdout_behavior = .Pipe; |
| 4965 | 4966 | child.stderr_behavior = .Pipe; |
| 4966 | 4967 | child.progress_node = child_progress_node; |
| 4968 | child.thread_pool = comp.thread_pool; | |
| 4967 | 4969 | |
| 4968 | 4970 | child.spawn() catch |err| { |
| 4969 | 4971 | return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) }); |
src/link.zig+1| ... | ... | @@ -1011,6 +1011,7 @@ pub fn spawnLld( |
| 1011 | 1011 | defer comp.gpa.free(stderr); |
| 1012 | 1012 | |
| 1013 | 1013 | var child = std.process.Child.init(argv, arena); |
| 1014 | child.thread_pool = comp.thread_pool; | |
| 1014 | 1015 | const term = (if (comp.clang_passthrough_mode) term: { |
| 1015 | 1016 | child.stdin_behavior = .Inherit; |
| 1016 | 1017 | child.stdout_behavior = .Inherit; |
src/main.zig+16-4| ... | ... | @@ -3136,7 +3136,9 @@ fn buildOutputType( |
| 3136 | 3136 | break :l global_cache_directory; |
| 3137 | 3137 | }; |
| 3138 | 3138 | |
| 3139 | var thread_pool = try std.zig.initThreadPool(gpa, .{ | |
| 3139 | var thread_pool: std.Thread.Pool = undefined; | |
| 3140 | try std.zig.initThreadPool(&thread_pool, .{ | |
| 3141 | .allocator = gpa, | |
| 3140 | 3142 | .cache_directory = local_cache_directory, |
| 3141 | 3143 | }); |
| 3142 | 3144 | defer thread_pool.deinit(); |
| ... | ... | @@ -4250,6 +4252,7 @@ fn runOrTest( |
| 4250 | 4252 | child.stdin_behavior = .Inherit; |
| 4251 | 4253 | child.stdout_behavior = .Inherit; |
| 4252 | 4254 | child.stderr_behavior = .Inherit; |
| 4255 | child.thread_pool = comp.thread_pool; | |
| 4253 | 4256 | |
| 4254 | 4257 | // Here we release all the locks associated with the Compilation so |
| 4255 | 4258 | // that whatever this child process wants to do won't deadlock. |
| ... | ... | @@ -4395,6 +4398,7 @@ fn runOrTestHotSwap( |
| 4395 | 4398 | child.stdin_behavior = .Inherit; |
| 4396 | 4399 | child.stdout_behavior = .Inherit; |
| 4397 | 4400 | child.stderr_behavior = .Inherit; |
| 4401 | child.thread_pool = comp.thread_pool; | |
| 4398 | 4402 | |
| 4399 | 4403 | try child.spawn(); |
| 4400 | 4404 | |
| ... | ... | @@ -4896,7 +4900,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 4896 | 4900 | |
| 4897 | 4901 | child_argv.items[argv_index_cache_dir] = local_cache_directory.path orelse cwd_path; |
| 4898 | 4902 | |
| 4899 | var thread_pool = try std.zig.initThreadPool(gpa, .{ | |
| 4903 | var thread_pool: std.Thread.Pool = undefined; | |
| 4904 | try std.zig.initThreadPool(&thread_pool, .{ | |
| 4905 | .allocator = gpa, | |
| 4900 | 4906 | .cache_directory = local_cache_directory, |
| 4901 | 4907 | }); |
| 4902 | 4908 | defer thread_pool.deinit(); |
| ... | ... | @@ -5185,6 +5191,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5185 | 5191 | child.stdin_behavior = .Inherit; |
| 5186 | 5192 | child.stdout_behavior = .Inherit; |
| 5187 | 5193 | child.stderr_behavior = .Inherit; |
| 5194 | child.thread_pool = &thread_pool; | |
| 5188 | 5195 | |
| 5189 | 5196 | const term = t: { |
| 5190 | 5197 | std.debug.lockStdErr(); |
| ... | ... | @@ -5331,7 +5338,9 @@ fn jitCmd( |
| 5331 | 5338 | }; |
| 5332 | 5339 | defer global_cache_directory.handle.close(); |
| 5333 | 5340 | |
| 5334 | var thread_pool = try std.zig.initThreadPool(gpa, .{ | |
| 5341 | var thread_pool: std.Thread.Pool = undefined; | |
| 5342 | try std.zig.initThreadPool(&thread_pool, .{ | |
| 5343 | .allocator = gpa, | |
| 5335 | 5344 | .cache_directory = global_cache_directory, |
| 5336 | 5345 | }); |
| 5337 | 5346 | defer thread_pool.deinit(); |
| ... | ... | @@ -5474,6 +5483,7 @@ fn jitCmd( |
| 5474 | 5483 | child.stdin_behavior = .Inherit; |
| 5475 | 5484 | child.stdout_behavior = if (options.capture == null) .Inherit else .Pipe; |
| 5476 | 5485 | child.stderr_behavior = .Inherit; |
| 5486 | child.thread_pool = &thread_pool; | |
| 5477 | 5487 | |
| 5478 | 5488 | try child.spawn(); |
| 5479 | 5489 | |
| ... | ... | @@ -6897,7 +6907,9 @@ fn cmdFetch( |
| 6897 | 6907 | }; |
| 6898 | 6908 | defer global_cache_directory.handle.close(); |
| 6899 | 6909 | |
| 6900 | var thread_pool = try std.zig.initThreadPool(gpa, .{ | |
| 6910 | var thread_pool: std.Thread.Pool = undefined; | |
| 6911 | try std.zig.initThreadPool(&thread_pool, .{ | |
| 6912 | .allocator = gpa, | |
| 6901 | 6913 | .cache_directory = global_cache_directory, |
| 6902 | 6914 | }); |
| 6903 | 6915 | defer thread_pool.deinit(); |