| author | |
| committer | |
| log | a72292513e378159c542a785ca69f8111bacbdcf |
| tree | aac9e20a88f90e9117b338bcc0e925c7ffa90182 |
| parent | a96b78c170ef0464e51a1c2fa226c51d49cfde04 |
This function accepts a WaitGroup parameter and manages the reference
counting therein. It also is infallible.
The existing `spawn` function is still handy when the job wants to
further schedule more tasks.6 files changed, 88 insertions(+), 86 deletions(-)
lib/compiler/build_runner.zig+6-11| ... | ... | @@ -466,10 +466,9 @@ fn runStepNames( |
| 466 | 466 | const step = steps_slice[steps_slice.len - i - 1]; |
| 467 | 467 | if (step.state == .skipped_oom) continue; |
| 468 | 468 | |
| 469 | wait_group.start(); | |
| 470 | thread_pool.spawn(workerMakeOneStep, .{ | |
| 469 | thread_pool.spawnWg(&wait_group, workerMakeOneStep, .{ | |
| 471 | 470 | &wait_group, &thread_pool, b, step, &step_prog, run, |
| 472 | }) catch @panic("OOM"); | |
| 471 | }); | |
| 473 | 472 | } |
| 474 | 473 | } |
| 475 | 474 | assert(run.memory_blocked_steps.items.len == 0); |
| ... | ... | @@ -895,8 +894,6 @@ fn workerMakeOneStep( |
| 895 | 894 | prog_node: *std.Progress.Node, |
| 896 | 895 | run: *Run, |
| 897 | 896 | ) void { |
| 898 | defer wg.finish(); | |
| 899 | ||
| 900 | 897 | // First, check the conditions for running this step. If they are not met, |
| 901 | 898 | // then we return without doing the step, relying on another worker to |
| 902 | 899 | // queue this step up again when dependencies are met. |
| ... | ... | @@ -976,10 +973,9 @@ fn workerMakeOneStep( |
| 976 | 973 | |
| 977 | 974 | // Successful completion of a step, so we queue up its dependants as well. |
| 978 | 975 | for (s.dependants.items) |dep| { |
| 979 | wg.start(); | |
| 980 | thread_pool.spawn(workerMakeOneStep, .{ | |
| 976 | thread_pool.spawnWg(wg, workerMakeOneStep, .{ | |
| 981 | 977 | wg, thread_pool, b, dep, prog_node, run, |
| 982 | }) catch @panic("OOM"); | |
| 978 | }); | |
| 983 | 979 | } |
| 984 | 980 | } |
| 985 | 981 | |
| ... | ... | @@ -1002,10 +998,9 @@ fn workerMakeOneStep( |
| 1002 | 998 | if (dep.max_rss <= remaining) { |
| 1003 | 999 | remaining -= dep.max_rss; |
| 1004 | 1000 | |
| 1005 | wg.start(); | |
| 1006 | thread_pool.spawn(workerMakeOneStep, .{ | |
| 1001 | thread_pool.spawnWg(wg, workerMakeOneStep, .{ | |
| 1007 | 1002 | wg, thread_pool, b, dep, prog_node, run, |
| 1008 | }) catch @panic("OOM"); | |
| 1003 | }); | |
| 1009 | 1004 | } else { |
| 1010 | 1005 | run.memory_blocked_steps.items[i] = dep; |
| 1011 | 1006 | i += 1; |
lib/std/Thread/Pool.zig+59| ... | ... | @@ -75,6 +75,65 @@ fn join(pool: *Pool, spawned: usize) void { |
| 75 | 75 | pool.allocator.free(pool.threads); |
| 76 | 76 | } |
| 77 | 77 | |
| 78 | /// Runs `func` in the thread pool, calling `WaitGroup.start` beforehand, and | |
| 79 | /// `WaitGroup.finish` after it returns. | |
| 80 | /// | |
| 81 | /// In the case that queuing the function call fails to allocate memory, or the | |
| 82 | /// target is single-threaded, the function is called directly. | |
| 83 | pub fn spawnWg(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, args: anytype) void { | |
| 84 | wait_group.start(); | |
| 85 | ||
| 86 | if (builtin.single_threaded) { | |
| 87 | @call(.auto, func, args); | |
| 88 | wait_group.finish(); | |
| 89 | return; | |
| 90 | } | |
| 91 | ||
| 92 | const Args = @TypeOf(args); | |
| 93 | const Closure = struct { | |
| 94 | arguments: Args, | |
| 95 | pool: *Pool, | |
| 96 | run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } }, | |
| 97 | wait_group: *WaitGroup, | |
| 98 | ||
| 99 | fn runFn(runnable: *Runnable) void { | |
| 100 | const run_node: *RunQueue.Node = @fieldParentPtr("data", runnable); | |
| 101 | const closure: *@This() = @alignCast(@fieldParentPtr("run_node", run_node)); | |
| 102 | @call(.auto, func, closure.arguments); | |
| 103 | closure.wait_group.finish(); | |
| 104 | ||
| 105 | // The thread pool's allocator is protected by the mutex. | |
| 106 | const mutex = &closure.pool.mutex; | |
| 107 | mutex.lock(); | |
| 108 | defer mutex.unlock(); | |
| 109 | ||
| 110 | closure.pool.allocator.destroy(closure); | |
| 111 | } | |
| 112 | }; | |
| 113 | ||
| 114 | { | |
| 115 | pool.mutex.lock(); | |
| 116 | ||
| 117 | const closure = pool.allocator.create(Closure) catch { | |
| 118 | pool.mutex.unlock(); | |
| 119 | @call(.auto, func, args); | |
| 120 | wait_group.finish(); | |
| 121 | return; | |
| 122 | }; | |
| 123 | closure.* = .{ | |
| 124 | .arguments = args, | |
| 125 | .pool = pool, | |
| 126 | .wait_group = wait_group, | |
| 127 | }; | |
| 128 | ||
| 129 | pool.run_queue.prepend(&closure.run_node); | |
| 130 | pool.mutex.unlock(); | |
| 131 | } | |
| 132 | ||
| 133 | // Notify waiting threads outside the lock to try and keep the critical section small. | |
| 134 | pool.cond.signal(); | |
| 135 | } | |
| 136 | ||
| 78 | 137 | pub fn spawn(pool: *Pool, comptime func: anytype, args: anytype) !void { |
| 79 | 138 | if (builtin.single_threaded) { |
| 80 | 139 | @call(.auto, func, args); |
src/Compilation.zig+14-46| ... | ... | @@ -3273,7 +3273,7 @@ pub fn performAllTheWork( |
| 3273 | 3273 | |
| 3274 | 3274 | if (!build_options.only_c and !build_options.only_core_functionality) { |
| 3275 | 3275 | if (comp.docs_emit != null) { |
| 3276 | try taskDocsCopy(comp, &comp.work_queue_wait_group); | |
| 3276 | comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerDocsCopy, .{comp}); | |
| 3277 | 3277 | comp.work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, &wasm_prog_node }); |
| 3278 | 3278 | } |
| 3279 | 3279 | } |
| ... | ... | @@ -3305,39 +3305,34 @@ pub fn performAllTheWork( |
| 3305 | 3305 | |
| 3306 | 3306 | const file = mod.builtin_file orelse continue; |
| 3307 | 3307 | |
| 3308 | comp.astgen_wait_group.start(); | |
| 3309 | try comp.thread_pool.spawn(workerUpdateBuiltinZigFile, .{ | |
| 3310 | comp, mod, file, &comp.astgen_wait_group, | |
| 3308 | comp.thread_pool.spawnWg(&comp.astgen_wait_group, workerUpdateBuiltinZigFile, .{ | |
| 3309 | comp, mod, file, | |
| 3311 | 3310 | }); |
| 3312 | 3311 | } |
| 3313 | 3312 | } |
| 3314 | 3313 | |
| 3315 | 3314 | while (comp.astgen_work_queue.readItem()) |file| { |
| 3316 | comp.astgen_wait_group.start(); | |
| 3317 | try comp.thread_pool.spawn(workerAstGenFile, .{ | |
| 3315 | comp.thread_pool.spawnWg(&comp.astgen_wait_group, workerAstGenFile, .{ | |
| 3318 | 3316 | comp, file, &zir_prog_node, &comp.astgen_wait_group, .root, |
| 3319 | 3317 | }); |
| 3320 | 3318 | } |
| 3321 | 3319 | |
| 3322 | 3320 | while (comp.embed_file_work_queue.readItem()) |embed_file| { |
| 3323 | comp.astgen_wait_group.start(); | |
| 3324 | try comp.thread_pool.spawn(workerCheckEmbedFile, .{ | |
| 3325 | comp, embed_file, &comp.astgen_wait_group, | |
| 3321 | comp.thread_pool.spawnWg(&comp.astgen_wait_group, workerCheckEmbedFile, .{ | |
| 3322 | comp, embed_file, | |
| 3326 | 3323 | }); |
| 3327 | 3324 | } |
| 3328 | 3325 | |
| 3329 | 3326 | while (comp.c_object_work_queue.readItem()) |c_object| { |
| 3330 | comp.work_queue_wait_group.start(); | |
| 3331 | try comp.thread_pool.spawn(workerUpdateCObject, .{ | |
| 3332 | comp, c_object, &c_obj_prog_node, &comp.work_queue_wait_group, | |
| 3327 | comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerUpdateCObject, .{ | |
| 3328 | comp, c_object, &c_obj_prog_node, | |
| 3333 | 3329 | }); |
| 3334 | 3330 | } |
| 3335 | 3331 | |
| 3336 | 3332 | if (!build_options.only_core_functionality) { |
| 3337 | 3333 | while (comp.win32_resource_work_queue.readItem()) |win32_resource| { |
| 3338 | comp.work_queue_wait_group.start(); | |
| 3339 | try comp.thread_pool.spawn(workerUpdateWin32Resource, .{ | |
| 3340 | comp, win32_resource, &win32_resource_prog_node, &comp.work_queue_wait_group, | |
| 3334 | comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerUpdateWin32Resource, .{ | |
| 3335 | comp, win32_resource, &win32_resource_prog_node, | |
| 3341 | 3336 | }); |
| 3342 | 3337 | } |
| 3343 | 3338 | } |
| ... | ... | @@ -3680,14 +3675,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v |
| 3680 | 3675 | } |
| 3681 | 3676 | } |
| 3682 | 3677 | |
| 3683 | fn taskDocsCopy(comp: *Compilation, wg: *WaitGroup) !void { | |
| 3684 | wg.start(); | |
| 3685 | errdefer wg.finish(); | |
| 3686 | try comp.thread_pool.spawn(workerDocsCopy, .{ comp, wg }); | |
| 3687 | } | |
| 3688 | ||
| 3689 | fn workerDocsCopy(comp: *Compilation, wg: *WaitGroup) void { | |
| 3690 | defer wg.finish(); | |
| 3678 | fn workerDocsCopy(comp: *Compilation) void { | |
| 3691 | 3679 | docsCopyFallible(comp) catch |err| { |
| 3692 | 3680 | return comp.lockAndSetMiscFailure( |
| 3693 | 3681 | .docs_copy, |
| ... | ... | @@ -3965,8 +3953,6 @@ fn workerAstGenFile( |
| 3965 | 3953 | wg: *WaitGroup, |
| 3966 | 3954 | src: AstGenSrc, |
| 3967 | 3955 | ) void { |
| 3968 | defer wg.finish(); | |
| 3969 | ||
| 3970 | 3956 | var child_prog_node = prog_node.start(file.sub_file_path, 0); |
| 3971 | 3957 | child_prog_node.activate(); |
| 3972 | 3958 | defer child_prog_node.end(); |
| ... | ... | @@ -4025,13 +4011,9 @@ fn workerAstGenFile( |
| 4025 | 4011 | .importing_file = file, |
| 4026 | 4012 | .import_tok = item.data.token, |
| 4027 | 4013 | } }; |
| 4028 | wg.start(); | |
| 4029 | comp.thread_pool.spawn(workerAstGenFile, .{ | |
| 4014 | comp.thread_pool.spawnWg(wg, workerAstGenFile, .{ | |
| 4030 | 4015 | comp, import_result.file, prog_node, wg, sub_src, |
| 4031 | }) catch { | |
| 4032 | wg.finish(); | |
| 4033 | continue; | |
| 4034 | }; | |
| 4016 | }); | |
| 4035 | 4017 | } |
| 4036 | 4018 | } |
| 4037 | 4019 | } |
| ... | ... | @@ -4041,9 +4023,7 @@ fn workerUpdateBuiltinZigFile( |
| 4041 | 4023 | comp: *Compilation, |
| 4042 | 4024 | mod: *Package.Module, |
| 4043 | 4025 | file: *Module.File, |
| 4044 | wg: *WaitGroup, | |
| 4045 | 4026 | ) void { |
| 4046 | defer wg.finish(); | |
| 4047 | 4027 | Builtin.populateFile(comp, mod, file) catch |err| { |
| 4048 | 4028 | comp.mutex.lock(); |
| 4049 | 4029 | defer comp.mutex.unlock(); |
| ... | ... | @@ -4054,13 +4034,7 @@ fn workerUpdateBuiltinZigFile( |
| 4054 | 4034 | }; |
| 4055 | 4035 | } |
| 4056 | 4036 | |
| 4057 | fn workerCheckEmbedFile( | |
| 4058 | comp: *Compilation, | |
| 4059 | embed_file: *Module.EmbedFile, | |
| 4060 | wg: *WaitGroup, | |
| 4061 | ) void { | |
| 4062 | defer wg.finish(); | |
| 4063 | ||
| 4037 | fn workerCheckEmbedFile(comp: *Compilation, embed_file: *Module.EmbedFile) void { | |
| 4064 | 4038 | comp.detectEmbedFileUpdate(embed_file) catch |err| { |
| 4065 | 4039 | comp.reportRetryableEmbedFileError(embed_file, err) catch |oom| switch (oom) { |
| 4066 | 4040 | // Swallowing this error is OK because it's implied to be OOM when |
| ... | ... | @@ -4289,10 +4263,7 @@ fn workerUpdateCObject( |
| 4289 | 4263 | comp: *Compilation, |
| 4290 | 4264 | c_object: *CObject, |
| 4291 | 4265 | progress_node: *std.Progress.Node, |
| 4292 | wg: *WaitGroup, | |
| 4293 | 4266 | ) void { |
| 4294 | defer wg.finish(); | |
| 4295 | ||
| 4296 | 4267 | comp.updateCObject(c_object, progress_node) catch |err| switch (err) { |
| 4297 | 4268 | error.AnalysisFail => return, |
| 4298 | 4269 | else => { |
| ... | ... | @@ -4309,10 +4280,7 @@ fn workerUpdateWin32Resource( |
| 4309 | 4280 | comp: *Compilation, |
| 4310 | 4281 | win32_resource: *Win32Resource, |
| 4311 | 4282 | progress_node: *std.Progress.Node, |
| 4312 | wg: *WaitGroup, | |
| 4313 | 4283 | ) void { |
| 4314 | defer wg.finish(); | |
| 4315 | ||
| 4316 | 4284 | comp.updateWin32Resource(win32_resource, progress_node) catch |err| switch (err) { |
| 4317 | 4285 | error.AnalysisFail => return, |
| 4318 | 4286 | else => { |
src/Package/Fetch.zig+5-22| ... | ... | @@ -722,14 +722,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { |
| 722 | 722 | const thread_pool = f.job_queue.thread_pool; |
| 723 | 723 | |
| 724 | 724 | for (new_fetches, prog_names) |*new_fetch, prog_name| { |
| 725 | f.job_queue.wait_group.start(); | |
| 726 | thread_pool.spawn(workerRun, .{ new_fetch, prog_name }) catch |err| switch (err) { | |
| 727 | error.OutOfMemory => { | |
| 728 | new_fetch.oom_flag = true; | |
| 729 | f.job_queue.wait_group.finish(); | |
| 730 | continue; | |
| 731 | }, | |
| 732 | }; | |
| 725 | thread_pool.spawnWg(&f.job_queue.wait_group, workerRun, .{ new_fetch, prog_name }); | |
| 733 | 726 | } |
| 734 | 727 | } |
| 735 | 728 | |
| ... | ... | @@ -750,8 +743,6 @@ pub fn relativePathDigest( |
| 750 | 743 | } |
| 751 | 744 | |
| 752 | 745 | pub fn workerRun(f: *Fetch, prog_name: []const u8) void { |
| 753 | defer f.job_queue.wait_group.finish(); | |
| 754 | ||
| 755 | 746 | var prog_node = f.prog_node.start(prog_name, 0); |
| 756 | 747 | defer prog_node.end(); |
| 757 | 748 | prog_node.activate(); |
| ... | ... | @@ -1477,10 +1468,7 @@ fn computeHash( |
| 1477 | 1468 | .fs_path = fs_path, |
| 1478 | 1469 | .failure = undefined, // to be populated by the worker |
| 1479 | 1470 | }; |
| 1480 | wait_group.start(); | |
| 1481 | try thread_pool.spawn(workerDeleteFile, .{ | |
| 1482 | root_dir, deleted_file, &wait_group, | |
| 1483 | }); | |
| 1471 | thread_pool.spawnWg(&wait_group, workerDeleteFile, .{ root_dir, deleted_file }); | |
| 1484 | 1472 | try deleted_files.append(deleted_file); |
| 1485 | 1473 | continue; |
| 1486 | 1474 | } |
| ... | ... | @@ -1507,10 +1495,7 @@ fn computeHash( |
| 1507 | 1495 | .hash = undefined, // to be populated by the worker |
| 1508 | 1496 | .failure = undefined, // to be populated by the worker |
| 1509 | 1497 | }; |
| 1510 | wait_group.start(); | |
| 1511 | try thread_pool.spawn(workerHashFile, .{ | |
| 1512 | root_dir, hashed_file, &wait_group, | |
| 1513 | }); | |
| 1498 | thread_pool.spawnWg(&wait_group, workerHashFile, .{ root_dir, hashed_file }); | |
| 1514 | 1499 | try all_files.append(hashed_file); |
| 1515 | 1500 | } |
| 1516 | 1501 | } |
| ... | ... | @@ -1602,13 +1587,11 @@ fn dumpHashInfo(all_files: []const *const HashedFile) !void { |
| 1602 | 1587 | try bw.flush(); |
| 1603 | 1588 | } |
| 1604 | 1589 | |
| 1605 | fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void { | |
| 1606 | defer wg.finish(); | |
| 1590 | fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile) void { | |
| 1607 | 1591 | hashed_file.failure = hashFileFallible(dir, hashed_file); |
| 1608 | 1592 | } |
| 1609 | 1593 | |
| 1610 | fn workerDeleteFile(dir: fs.Dir, deleted_file: *DeletedFile, wg: *WaitGroup) void { | |
| 1611 | defer wg.finish(); | |
| 1594 | fn workerDeleteFile(dir: fs.Dir, deleted_file: *DeletedFile) void { | |
| 1612 | 1595 | deleted_file.failure = deleteFileFallible(dir, deleted_file); |
| 1613 | 1596 | } |
| 1614 | 1597 |
src/link/MachO/hasher.zig+1-5| ... | ... | @@ -36,14 +36,12 @@ pub fn ParallelHasher(comptime Hasher: type) type { |
| 36 | 36 | file_size - fstart |
| 37 | 37 | else |
| 38 | 38 | chunk_size; |
| 39 | wg.start(); | |
| 40 | try self.thread_pool.spawn(worker, .{ | |
| 39 | self.thread_pool.spawnWg(&wg, worker, .{ | |
| 41 | 40 | file, |
| 42 | 41 | fstart, |
| 43 | 42 | buffer[fstart..][0..fsize], |
| 44 | 43 | &(out_buf.*), |
| 45 | 44 | &(result.*), |
| 46 | &wg, | |
| 47 | 45 | }); |
| 48 | 46 | } |
| 49 | 47 | } |
| ... | ... | @@ -56,9 +54,7 @@ pub fn ParallelHasher(comptime Hasher: type) type { |
| 56 | 54 | buffer: []u8, |
| 57 | 55 | out: *[hash_size]u8, |
| 58 | 56 | err: *fs.File.PReadError!usize, |
| 59 | wg: *WaitGroup, | |
| 60 | 57 | ) void { |
| 61 | defer wg.finish(); | |
| 62 | 58 | err.* = file.preadAll(buffer, fstart); |
| 63 | 59 | Hasher.hash(buffer, out, .{}); |
| 64 | 60 | } |
src/main.zig+3-2| ... | ... | @@ -5109,8 +5109,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5109 | 5109 | &fetch, |
| 5110 | 5110 | ); |
| 5111 | 5111 | |
| 5112 | job_queue.wait_group.start(); | |
| 5113 | try job_queue.thread_pool.spawn(Package.Fetch.workerRun, .{ &fetch, "root" }); | |
| 5112 | job_queue.thread_pool.spawnWg(&job_queue.wait_group, Package.Fetch.workerRun, .{ | |
| 5113 | &fetch, "root", | |
| 5114 | }); | |
| 5114 | 5115 | job_queue.wait_group.wait(); |
| 5115 | 5116 | |
| 5116 | 5117 | try job_queue.consolidateErrors(); |