| author | |
| committer | |
| log | 2adfd4d107f071f91608bef22c7e91b1a9a93470 |
| tree | ef9da0bd175a2b4ddd12842e44df243606f353bf |
| parent | f862762f091637ad0d17c4735226c0c477c2cb3e |
Rename `wait` to `await` to be consistent with Future API. The
convention here is that this set of functionality goes together:
* async/concurrent
* await/cancel
Also rename Select `wait` to `await` for the same reason.
`Group.await` now can return `error.Canceled`. Furthermore,
`Group.await` does not auto-propagate cancelation. Instead, users should
follow the pattern of `defer group.cancel(io);` after initialization,
and doing `try group.await(io);` at the end of the success path.
Advanced logic can choose to do something other than this pattern in the
event of cancelation.
Additionally, fixes a bug in `std.Io.Threaded` future await, in which it
swallowed an `error.Canceled`. Now if a task is canceled while awaiting
a future, after propagating the cancel request, it also recancels,
meaning that the awaiting task will properly detect its own cancelation
at the next cancelation point.
Furthermore, fixes a bug in the compiler where `error.Canceled` was
being swallowed in `dispatchPrelinkWork`.
Finally, fixes std.crypto code that inappropriately used
`catch unreachable` in response to cancelation without even so much as a
comment explaining why it was believed to be unreachable. Now, those
functions have `error.Canceled` in the error set and propagate
cancelation properly.
With this way of doing things, `Group.await` has a nice property: even if
all tasks in the group are CPU bound and without cancelation points, the
`Group.await` can still be canceled. In such case, the task that was
waiting for `await` wakes up with a chance to do some more resource
cleanup tasks, such as canceling more things, before entering the
deferred `Group.cancel` call at which point it has to suspend until the
canceled but uninterruptible CPU bound tasks complete.
closes #3060116 files changed, 108 insertions(+), 78 deletions(-)
lib/compiler/build_runner.zig+5-3| ... | @@ -748,7 +748,7 @@ fn runStepNames( | ... | @@ -748,7 +748,7 @@ fn runStepNames( |
| 748 | defer step_prog.end(); | 748 | defer step_prog.end(); |
| 749 | 749 | ||
| 750 | var group: Io.Group = .init; | 750 | var group: Io.Group = .init; |
| 751 | defer group.wait(io); | 751 | defer group.cancel(io); |
| 752 | 752 | ||
| 753 | // Here we spawn the initial set of tasks with a nice heuristic - | 753 | // Here we spawn the initial set of tasks with a nice heuristic - |
| 754 | // dependency order. Each worker when it finishes a step will then | 754 | // dependency order. Each worker when it finishes a step will then |
| ... | @@ -760,6 +760,8 @@ fn runStepNames( | ... | @@ -760,6 +760,8 @@ fn runStepNames( |
| 760 | 760 | ||
| 761 | group.async(io, workerMakeOneStep, .{ &group, b, step, step_prog, run }); | 761 | group.async(io, workerMakeOneStep, .{ &group, b, step, step_prog, run }); |
| 762 | } | 762 | } |
| 763 | |||
| 764 | try group.await(io); | ||
| 763 | } | 765 | } |
| 764 | 766 | ||
| 765 | assert(run.memory_blocked_steps.items.len == 0); | 767 | assert(run.memory_blocked_steps.items.len == 0); |
| ... | @@ -820,7 +822,7 @@ fn runStepNames( | ... | @@ -820,7 +822,7 @@ fn runStepNames( |
| 820 | // * Memory-mapping to share data between the fuzzer and build runner. | 822 | // * Memory-mapping to share data between the fuzzer and build runner. |
| 821 | // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving | 823 | // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving |
| 822 | // many addresses to source locations). | 824 | // many addresses to source locations). |
| 823 | .windows => fatal("--fuzz not yet implemented for {s}", .{@tagName(builtin.os.tag)}), | 825 | .windows => fatal("--fuzz not yet implemented for {t}", .{builtin.os.tag}), |
| 824 | else => {}, | 826 | else => {}, |
| 825 | } | 827 | } |
| 826 | if (@bitSizeOf(usize) != 64) { | 828 | if (@bitSizeOf(usize) != 64) { |
| ... | @@ -843,7 +845,7 @@ fn runStepNames( | ... | @@ -843,7 +845,7 @@ fn runStepNames( |
| 843 | step_stack.keys(), | 845 | step_stack.keys(), |
| 844 | parent_prog_node, | 846 | parent_prog_node, |
| 845 | mode, | 847 | mode, |
| 846 | ) catch |err| fatal("failed to start fuzzer: {s}", .{@errorName(err)}); | 848 | ) catch |err| fatal("failed to start fuzzer: {t}", .{err}); |
| 847 | defer f.deinit(); | 849 | defer f.deinit(); |
| 848 | 850 | ||
| 849 | f.start(); | 851 | f.start(); |
lib/std/Build/Fuzz.zig+3-3| ... | @@ -78,7 +78,7 @@ pub fn init( | ... | @@ -78,7 +78,7 @@ pub fn init( |
| 78 | all_steps: []const *Build.Step, | 78 | all_steps: []const *Build.Step, |
| 79 | root_prog_node: std.Progress.Node, | 79 | root_prog_node: std.Progress.Node, |
| 80 | mode: Mode, | 80 | mode: Mode, |
| 81 | ) Allocator.Error!Fuzz { | 81 | ) error{ OutOfMemory, Canceled }!Fuzz { |
| 82 | const run_steps: []const *Step.Run = steps: { | 82 | const run_steps: []const *Step.Run = steps: { |
| 83 | var steps: std.ArrayList(*Step.Run) = .empty; | 83 | var steps: std.ArrayList(*Step.Run) = .empty; |
| 84 | defer steps.deinit(gpa); | 84 | defer steps.deinit(gpa); |
| ... | @@ -98,7 +98,7 @@ pub fn init( | ... | @@ -98,7 +98,7 @@ pub fn init( |
| 98 | if (steps.items.len == 0) fatal("no fuzz tests found", .{}); | 98 | if (steps.items.len == 0) fatal("no fuzz tests found", .{}); |
| 99 | rebuild_node.setEstimatedTotalItems(steps.items.len); | 99 | rebuild_node.setEstimatedTotalItems(steps.items.len); |
| 100 | const run_steps = try gpa.dupe(*Step.Run, steps.items); | 100 | const run_steps = try gpa.dupe(*Step.Run, steps.items); |
| 101 | rebuild_group.wait(io); | 101 | try rebuild_group.await(io); |
| 102 | break :steps run_steps; | 102 | break :steps run_steps; |
| 103 | }; | 103 | }; |
| 104 | errdefer gpa.free(run_steps); | 104 | errdefer gpa.free(run_steps); |
| ... | @@ -517,7 +517,7 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void { | ... | @@ -517,7 +517,7 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void { |
| 517 | assert(fuzz.mode == .limit); | 517 | assert(fuzz.mode == .limit); |
| 518 | const io = fuzz.io; | 518 | const io = fuzz.io; |
| 519 | 519 | ||
| 520 | fuzz.group.wait(io); | 520 | fuzz.group.awaitUncancelable(io); |
| 521 | fuzz.group = .init; | 521 | fuzz.group = .init; |
| 522 | 522 | ||
| 523 | std.debug.print("======= FUZZING REPORT =======\n", .{}); | 523 | std.debug.print("======= FUZZING REPORT =======\n", .{}); |
lib/std/Io.zig+23-12| ... | @@ -436,7 +436,7 @@ pub fn Poller(comptime StreamEnum: type) type { | ... | @@ -436,7 +436,7 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 436 | // Cancel the pending read into the FIFO. | 436 | // Cancel the pending read into the FIFO. |
| 437 | _ = windows.kernel32.CancelIo(handle); | 437 | _ = windows.kernel32.CancelIo(handle); |
| 438 | 438 | ||
| 439 | // We have to wait for the handle to be signalled, i.e. for the cancellation to complete. | 439 | // We have to wait for the handle to be signalled, i.e. for the cancelation to complete. |
| 440 | switch (windows.kernel32.WaitForSingleObject(handle, windows.INFINITE)) { | 440 | switch (windows.kernel32.WaitForSingleObject(handle, windows.INFINITE)) { |
| 441 | windows.WAIT_OBJECT_0 => {}, | 441 | windows.WAIT_OBJECT_0 => {}, |
| 442 | windows.WAIT_FAILED => return windows.unexpectedError(windows.GetLastError()), | 442 | windows.WAIT_FAILED => return windows.unexpectedError(windows.GetLastError()), |
| ... | @@ -644,7 +644,7 @@ pub const VTable = struct { | ... | @@ -644,7 +644,7 @@ pub const VTable = struct { |
| 644 | context_alignment: std.mem.Alignment, | 644 | context_alignment: std.mem.Alignment, |
| 645 | start: *const fn (*Group, context: *const anyopaque) void, | 645 | start: *const fn (*Group, context: *const anyopaque) void, |
| 646 | ) ConcurrentError!void, | 646 | ) ConcurrentError!void, |
| 647 | groupWait: *const fn (?*anyopaque, *Group, token: *anyopaque) void, | 647 | groupAwait: *const fn (?*anyopaque, *Group, token: *anyopaque) Cancelable!void, |
| 648 | groupCancel: *const fn (?*anyopaque, *Group, token: *anyopaque) void, | 648 | groupCancel: *const fn (?*anyopaque, *Group, token: *anyopaque) void, |
| 649 | 649 | ||
| 650 | recancel: *const fn (?*anyopaque) void, | 650 | recancel: *const fn (?*anyopaque) void, |
| ... | @@ -1023,7 +1023,7 @@ pub fn Future(Result: type) type { | ... | @@ -1023,7 +1023,7 @@ pub fn Future(Result: type) type { |
| 1023 | any_future: ?*AnyFuture, | 1023 | any_future: ?*AnyFuture, |
| 1024 | result: Result, | 1024 | result: Result, |
| 1025 | 1025 | ||
| 1026 | /// Equivalent to `await` but places a cancellation request. This causes the task to receive | 1026 | /// Equivalent to `await` but places a cancelation request. This causes the task to receive |
| 1027 | /// `error.Canceled` from its next "cancelation point" (if any). A cancelation point is a | 1027 | /// `error.Canceled` from its next "cancelation point" (if any). A cancelation point is a |
| 1028 | /// call to a function in `Io` which can return `error.Canceled`. | 1028 | /// call to a function in `Io` which can return `error.Canceled`. |
| 1029 | /// | 1029 | /// |
| ... | @@ -1071,7 +1071,7 @@ pub const Group = struct { | ... | @@ -1071,7 +1071,7 @@ pub const Group = struct { |
| 1071 | /// already been called and completed, or it has successfully been assigned | 1071 | /// already been called and completed, or it has successfully been assigned |
| 1072 | /// a unit of concurrency. | 1072 | /// a unit of concurrency. |
| 1073 | /// | 1073 | /// |
| 1074 | /// After this is called, `wait` or `cancel` must be called before the | 1074 | /// After this is called, `await` or `cancel` must be called before the |
| 1075 | /// group is deinitialized. | 1075 | /// group is deinitialized. |
| 1076 | /// | 1076 | /// |
| 1077 | /// Threadsafe. | 1077 | /// Threadsafe. |
| ... | @@ -1092,11 +1092,11 @@ pub const Group = struct { | ... | @@ -1092,11 +1092,11 @@ pub const Group = struct { |
| 1092 | } | 1092 | } |
| 1093 | 1093 | ||
| 1094 | /// Calls `function` with `args`, such that the function is not guaranteed | 1094 | /// Calls `function` with `args`, such that the function is not guaranteed |
| 1095 | /// to have returned until `wait` is called, allowing the caller to | 1095 | /// to have returned until `await` is called, allowing the caller to |
| 1096 | /// progress while waiting for any `Io` operations. | 1096 | /// progress while waiting for any `Io` operations. |
| 1097 | /// | 1097 | /// |
| 1098 | /// The resource spawned is owned by the group; after this is called, | 1098 | /// The resource spawned is owned by the group; after this is called, |
| 1099 | /// `wait` or `cancel` must be called before the group is deinitialized. | 1099 | /// `await` or `cancel` must be called before the group is deinitialized. |
| 1100 | /// | 1100 | /// |
| 1101 | /// This has stronger guarantee than `async`, placing restrictions on what kind | 1101 | /// This has stronger guarantee than `async`, placing restrictions on what kind |
| 1102 | /// of `Io` implementations are supported. By calling `async` instead, one | 1102 | /// of `Io` implementations are supported. By calling `async` instead, one |
| ... | @@ -1120,20 +1120,31 @@ pub const Group = struct { | ... | @@ -1120,20 +1120,31 @@ pub const Group = struct { |
| 1120 | } | 1120 | } |
| 1121 | 1121 | ||
| 1122 | /// Blocks until all tasks of the group finish. During this time, | 1122 | /// Blocks until all tasks of the group finish. During this time, |
| 1123 | /// cancellation requests propagate to all members of the group. | 1123 | /// cancelation requests propagate to all members of the group. |
| 1124 | /// | 1124 | /// |
| 1125 | /// Idempotent. Not threadsafe. | 1125 | /// Idempotent. Not threadsafe. |
| 1126 | /// | 1126 | /// |
| 1127 | /// It is safe to call this function concurrently with `Group.async` or | 1127 | /// It is safe to call this function concurrently with `Group.async` or |
| 1128 | /// `Group.concurrent`, provided that the group does not complete until | 1128 | /// `Group.concurrent`, provided that the group does not complete until |
| 1129 | /// the call to `Group.async` or `Group.concurrent` returns. | 1129 | /// the call to `Group.async` or `Group.concurrent` returns. |
| 1130 | pub fn wait(g: *Group, io: Io) void { | 1130 | pub fn await(g: *Group, io: Io) Cancelable!void { |
| 1131 | const token = g.token.load(.acquire) orelse return; | 1131 | const token = g.token.load(.acquire) orelse return; |
| 1132 | io.vtable.groupWait(io.userdata, g, token); | 1132 | try io.vtable.groupAwait(io.userdata, g, token); |
| 1133 | assert(g.token.raw == null); | 1133 | assert(g.token.raw == null); |
| 1134 | } | 1134 | } |
| 1135 | 1135 | ||
| 1136 | /// Equivalent to `wait` but immediately requests cancellation on all | 1136 | /// Equivalent to `await` but temporarily blocks cancelation while waiting. |
| 1137 | pub fn awaitUncancelable(g: *Group, io: Io) void { | ||
| 1138 | const token = g.token.load(.acquire) orelse return; | ||
| 1139 | const prev = swapCancelProtection(io, .blocked); | ||
| 1140 | defer _ = swapCancelProtection(io, prev); | ||
| 1141 | io.vtable.groupAwait(io.userdata, g, token) catch |err| switch (err) { | ||
| 1142 | error.Canceled => unreachable, | ||
| 1143 | }; | ||
| 1144 | assert(g.token.raw == null); | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | /// Equivalent to `await` but immediately requests cancelation on all | ||
| 1137 | /// members of the group. | 1148 | /// members of the group. |
| 1138 | /// | 1149 | /// |
| 1139 | /// For a description of cancelation and cancelation points, see `Future.cancel`. | 1150 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| ... | @@ -1272,7 +1283,7 @@ pub fn Select(comptime U: type) type { | ... | @@ -1272,7 +1283,7 @@ pub fn Select(comptime U: type) type { |
| 1272 | /// Asserts there is at least one more `outstanding` task. | 1283 | /// Asserts there is at least one more `outstanding` task. |
| 1273 | /// | 1284 | /// |
| 1274 | /// Not threadsafe. | 1285 | /// Not threadsafe. |
| 1275 | pub fn wait(s: *S) Cancelable!U { | 1286 | pub fn await(s: *S) Cancelable!U { |
| 1276 | s.outstanding -= 1; | 1287 | s.outstanding -= 1; |
| 1277 | return s.queue.getOne(s.io) catch |err| switch (err) { | 1288 | return s.queue.getOne(s.io) catch |err| switch (err) { |
| 1278 | error.Canceled => |e| return e, | 1289 | error.Canceled => |e| return e, |
| ... | @@ -1280,7 +1291,7 @@ pub fn Select(comptime U: type) type { | ... | @@ -1280,7 +1291,7 @@ pub fn Select(comptime U: type) type { |
| 1280 | }; | 1291 | }; |
| 1281 | } | 1292 | } |
| 1282 | 1293 | ||
| 1283 | /// Equivalent to `wait` but requests cancellation on all remaining | 1294 | /// Equivalent to `wait` but requests cancelation on all remaining |
| 1284 | /// tasks owned by the select. | 1295 | /// tasks owned by the select. |
| 1285 | /// | 1296 | /// |
| 1286 | /// For a description of cancelation and cancelation points, see `Future.cancel`. | 1297 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
lib/std/Io/Threaded.zig+12-13| ... | @@ -795,7 +795,7 @@ pub fn io(t: *Threaded) Io { | ... | @@ -795,7 +795,7 @@ pub fn io(t: *Threaded) Io { |
| 795 | 795 | ||
| 796 | .groupAsync = groupAsync, | 796 | .groupAsync = groupAsync, |
| 797 | .groupConcurrent = groupConcurrent, | 797 | .groupConcurrent = groupConcurrent, |
| 798 | .groupWait = groupWait, | 798 | .groupAwait = groupAwait, |
| 799 | .groupCancel = groupCancel, | 799 | .groupCancel = groupCancel, |
| 800 | 800 | ||
| 801 | .recancel = recancel, | 801 | .recancel = recancel, |
| ... | @@ -933,7 +933,7 @@ pub fn ioBasic(t: *Threaded) Io { | ... | @@ -933,7 +933,7 @@ pub fn ioBasic(t: *Threaded) Io { |
| 933 | 933 | ||
| 934 | .groupAsync = groupAsync, | 934 | .groupAsync = groupAsync, |
| 935 | .groupConcurrent = groupConcurrent, | 935 | .groupConcurrent = groupConcurrent, |
| 936 | .groupWait = groupWait, | 936 | .groupAwait = groupAwait, |
| 937 | .groupCancel = groupCancel, | 937 | .groupCancel = groupCancel, |
| 938 | 938 | ||
| 939 | .recancel = recancel, | 939 | .recancel = recancel, |
| ... | @@ -1166,6 +1166,7 @@ const AsyncClosure = struct { | ... | @@ -1166,6 +1166,7 @@ const AsyncClosure = struct { |
| 1166 | error.Canceled => { | 1166 | error.Canceled => { |
| 1167 | ac.closure.requestCancel(t); | 1167 | ac.closure.requestCancel(t); |
| 1168 | ac.event.waitUncancelable(ioBasic(t)); | 1168 | ac.event.waitUncancelable(ioBasic(t)); |
| 1169 | recancel(t); | ||
| 1169 | }, | 1170 | }, |
| 1170 | }; | 1171 | }; |
| 1171 | @memcpy(result, ac.resultPointer()[0..result.len]); | 1172 | @memcpy(result, ac.resultPointer()[0..result.len]); |
| ... | @@ -1452,7 +1453,7 @@ fn groupConcurrent( | ... | @@ -1452,7 +1453,7 @@ fn groupConcurrent( |
| 1452 | t.cond.signal(); | 1453 | t.cond.signal(); |
| 1453 | } | 1454 | } |
| 1454 | 1455 | ||
| 1455 | fn groupWait(userdata: ?*anyopaque, group: *Io.Group, initial_token: *anyopaque) void { | 1456 | fn groupAwait(userdata: ?*anyopaque, group: *Io.Group, initial_token: *anyopaque) Io.Cancelable!void { |
| 1456 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 1457 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 1457 | const gpa = t.allocator; | 1458 | const gpa = t.allocator; |
| 1458 | 1459 | ||
| ... | @@ -1464,16 +1465,14 @@ fn groupWait(userdata: ?*anyopaque, group: *Io.Group, initial_token: *anyopaque) | ... | @@ -1464,16 +1465,14 @@ fn groupWait(userdata: ?*anyopaque, group: *Io.Group, initial_token: *anyopaque) |
| 1464 | const event: *Io.Event = @ptrCast(&group.context); | 1465 | const event: *Io.Event = @ptrCast(&group.context); |
| 1465 | const prev_state = group_state.fetchAdd(GroupClosure.sync_is_waiting, .acquire); | 1466 | const prev_state = group_state.fetchAdd(GroupClosure.sync_is_waiting, .acquire); |
| 1466 | assert(prev_state & GroupClosure.sync_is_waiting == 0); | 1467 | assert(prev_state & GroupClosure.sync_is_waiting == 0); |
| 1467 | if ((prev_state / GroupClosure.sync_one_pending) > 0) event.wait(ioBasic(t)) catch |err| switch (err) { | 1468 | { |
| 1468 | error.Canceled => { | 1469 | errdefer _ = group_state.fetchSub(GroupClosure.sync_is_waiting, .monotonic); |
| 1469 | var it: ?*std.SinglyLinkedList.Node = @ptrCast(@alignCast(group.token.load(.monotonic))); | 1470 | // This event.wait can return error.Canceled, in which case this logic does |
| 1470 | while (it) |node| : (it = node.next) { | 1471 | // *not* propagate cancel requests to each group member. Instead, the user |
| 1471 | const gc: *GroupClosure = @fieldParentPtr("node", node); | 1472 | // code will likely do this with a defered call to groupCancel, or, |
| 1472 | gc.closure.requestCancel(t); | 1473 | // intentionally not do this. |
| 1473 | } | 1474 | if ((prev_state / GroupClosure.sync_one_pending) > 0) try event.wait(ioBasic(t)); |
| 1474 | event.waitUncancelable(ioBasic(t)); | 1475 | } |
| 1475 | }, | ||
| 1476 | }; | ||
| 1477 | 1476 | ||
| 1478 | // Since the group has now finished, it's illegal to add more tasks to it until we return. It's | 1477 | // Since the group has now finished, it's illegal to add more tasks to it until we return. It's |
| 1479 | // also illegal for us to race with another `await` or `cancel`. Therefore, we must be the only | 1478 | // also illegal for us to race with another `await` or `cancel`. Therefore, we must be the only |
lib/std/Io/Threaded/test.zig+1-1| ... | @@ -124,7 +124,7 @@ test "Group.async context alignment" { | ... | @@ -124,7 +124,7 @@ test "Group.async context alignment" { |
| 124 | var group: std.Io.Group = .init; | 124 | var group: std.Io.Group = .init; |
| 125 | var result: ByteArray512 = undefined; | 125 | var result: ByteArray512 = undefined; |
| 126 | group.async(io, concatByteArraysResultPtr, .{ a, b, &result }); | 126 | group.async(io, concatByteArraysResultPtr, .{ a, b, &result }); |
| 127 | group.wait(io); | 127 | group.awaitUncancelable(io); |
| 128 | try std.testing.expectEqualSlices(u8, &expected.x, &result.x); | 128 | try std.testing.expectEqualSlices(u8, &expected.x, &result.x); |
| 129 | } | 129 | } |
| 130 | 130 |
lib/std/Io/net/HostName.zig+1-1| ... | @@ -289,7 +289,7 @@ pub fn connectMany( | ... | @@ -289,7 +289,7 @@ pub fn connectMany( |
| 289 | } else |err| switch (err) { | 289 | } else |err| switch (err) { |
| 290 | error.Canceled => |e| return e, | 290 | error.Canceled => |e| return e, |
| 291 | error.Closed => { | 291 | error.Closed => { |
| 292 | group.wait(io); | 292 | try group.await(io); |
| 293 | return lookup_future.await(io); | 293 | return lookup_future.await(io); |
| 294 | }, | 294 | }, |
| 295 | } | 295 | } |
lib/std/Io/test.zig+2-2| ... | @@ -194,7 +194,7 @@ test "Group" { | ... | @@ -194,7 +194,7 @@ test "Group" { |
| 194 | group.async(io, count, .{ 1, 10, &results[0] }); | 194 | group.async(io, count, .{ 1, 10, &results[0] }); |
| 195 | group.async(io, count, .{ 20, 30, &results[1] }); | 195 | group.async(io, count, .{ 20, 30, &results[1] }); |
| 196 | 196 | ||
| 197 | group.wait(io); | 197 | group.awaitUncancelable(io); |
| 198 | 198 | ||
| 199 | try testing.expectEqualSlices(usize, &.{ 45, 245 }, &results); | 199 | try testing.expectEqualSlices(usize, &.{ 45, 245 }, &results); |
| 200 | } | 200 | } |
| ... | @@ -249,7 +249,7 @@ test "Group concurrent" { | ... | @@ -249,7 +249,7 @@ test "Group concurrent" { |
| 249 | }, | 249 | }, |
| 250 | }; | 250 | }; |
| 251 | 251 | ||
| 252 | group.wait(io); | 252 | try group.await(io); |
| 253 | 253 | ||
| 254 | try testing.expectEqualSlices(usize, &.{ 45, 245 }, &results); | 254 | try testing.expectEqualSlices(usize, &.{ 45, 245 }, &results); |
| 255 | } | 255 | } |
lib/std/crypto.zig+1-1| ... | @@ -184,7 +184,7 @@ pub const pwhash = struct { | ... | @@ -184,7 +184,7 @@ pub const pwhash = struct { |
| 184 | 184 | ||
| 185 | pub const Error = HasherError || error{AllocatorRequired}; | 185 | pub const Error = HasherError || error{AllocatorRequired}; |
| 186 | pub const HasherError = KdfError || phc_format.Error; | 186 | pub const HasherError = KdfError || phc_format.Error; |
| 187 | pub const KdfError = errors.Error || std.mem.Allocator.Error || std.Thread.SpawnError; | 187 | pub const KdfError = errors.Error || std.mem.Allocator.Error || std.Thread.SpawnError || std.Io.Cancelable; |
| 188 | 188 | ||
| 189 | pub const argon2 = @import("crypto/argon2.zig"); | 189 | pub const argon2 = @import("crypto/argon2.zig"); |
| 190 | pub const bcrypt = @import("crypto/bcrypt.zig"); | 190 | pub const bcrypt = @import("crypto/bcrypt.zig"); |
lib/std/crypto/argon2.zig+16-14| ... | @@ -2,9 +2,9 @@ | ... | @@ -2,9 +2,9 @@ |
| 2 | // https://github.com/golang/crypto/tree/master/argon2 | 2 | // https://github.com/golang/crypto/tree/master/argon2 |
| 3 | // https://github.com/P-H-C/phc-winner-argon2 | 3 | // https://github.com/P-H-C/phc-winner-argon2 |
| 4 | 4 | ||
| 5 | const std = @import("std"); | ||
| 6 | const builtin = @import("builtin"); | 5 | const builtin = @import("builtin"); |
| 7 | 6 | ||
| 7 | const std = @import("std"); | ||
| 8 | const blake2 = crypto.hash.blake2; | 8 | const blake2 = crypto.hash.blake2; |
| 9 | const crypto = std.crypto; | 9 | const crypto = std.crypto; |
| 10 | const Io = std.Io; | 10 | const Io = std.Io; |
| ... | @@ -53,23 +53,24 @@ pub const Mode = enum { | ... | @@ -53,23 +53,24 @@ pub const Mode = enum { |
| 53 | pub const Params = struct { | 53 | pub const Params = struct { |
| 54 | const Self = @This(); | 54 | const Self = @This(); |
| 55 | 55 | ||
| 56 | /// A [t]ime cost, which defines the amount of computation realized and therefore the execution | 56 | /// Time cost, which defines the amount of computation realized and therefore the execution |
| 57 | /// time, given in number of iterations. | 57 | /// time, given in number of iterations. |
| 58 | t: u32, | 58 | t: u32, |
| 59 | 59 | ||
| 60 | /// A [m]emory cost, which defines the memory usage, given in kibibytes. | 60 | /// Memory cost, which defines the memory usage, given in kibibytes. |
| 61 | m: u32, | 61 | m: u32, |
| 62 | 62 | ||
| 63 | /// A [p]arallelism degree, which defines the number of parallel threads. | 63 | /// Parallelism degree, which defines the number of independent tasks, |
| 64 | /// to be multiplexed onto threads when possible. | ||
| 64 | p: u24, | 65 | p: u24, |
| 65 | 66 | ||
| 66 | /// The [secret] parameter, which is used for keyed hashing. This allows a secret key to be input | 67 | /// The secret parameter, which is used for keyed hashing. This allows a secret key to be input |
| 67 | /// at hashing time (from some external location) and be folded into the value of the hash. This | 68 | /// at hashing time (from some external location) and be folded into the value of the hash. This |
| 68 | /// means that even if your salts and hashes are compromised, an attacker cannot brute-force to | 69 | /// means that even if your salts and hashes are compromised, an attacker cannot brute-force to |
| 69 | /// find the password without the key. | 70 | /// find the password without the key. |
| 70 | secret: ?[]const u8 = null, | 71 | secret: ?[]const u8 = null, |
| 71 | 72 | ||
| 72 | /// The [ad] parameter, which is used to fold any additional data into the hash value. Functionally, | 73 | /// The ad parameter, which is used to fold any additional data into the hash value. Functionally, |
| 73 | /// this behaves almost exactly like the secret or salt parameters; the ad parameter is folding | 74 | /// this behaves almost exactly like the secret or salt parameters; the ad parameter is folding |
| 74 | /// into the value of the hash. However, this parameter is used for different data. The salt | 75 | /// into the value of the hash. However, this parameter is used for different data. The salt |
| 75 | /// should be a random string stored alongside your password. The secret should be a random key | 76 | /// should be a random string stored alongside your password. The secret should be a random key |
| ... | @@ -209,18 +210,18 @@ fn processBlocks( | ... | @@ -209,18 +210,18 @@ fn processBlocks( |
| 209 | threads: u24, | 210 | threads: u24, |
| 210 | mode: Mode, | 211 | mode: Mode, |
| 211 | io: Io, | 212 | io: Io, |
| 212 | ) void { | 213 | ) Io.Cancelable!void { |
| 213 | const lanes = memory / threads; | 214 | const lanes = memory / threads; |
| 214 | const segments = lanes / sync_points; | 215 | const segments = lanes / sync_points; |
| 215 | 216 | ||
| 216 | if (builtin.single_threaded or threads == 1) { | 217 | if (builtin.single_threaded or threads == 1) { |
| 217 | processBlocksSt(blocks, time, memory, threads, mode, lanes, segments); | 218 | processBlocksSync(blocks, time, memory, threads, mode, lanes, segments); |
| 218 | } else { | 219 | } else { |
| 219 | processBlocksMt(blocks, time, memory, threads, mode, lanes, segments, io); | 220 | try processBlocksAsync(blocks, time, memory, threads, mode, lanes, segments, io); |
| 220 | } | 221 | } |
| 221 | } | 222 | } |
| 222 | 223 | ||
| 223 | fn processBlocksSt( | 224 | fn processBlocksSync( |
| 224 | blocks: *Blocks, | 225 | blocks: *Blocks, |
| 225 | time: u32, | 226 | time: u32, |
| 226 | memory: u32, | 227 | memory: u32, |
| ... | @@ -241,7 +242,7 @@ fn processBlocksSt( | ... | @@ -241,7 +242,7 @@ fn processBlocksSt( |
| 241 | } | 242 | } |
| 242 | } | 243 | } |
| 243 | 244 | ||
| 244 | fn processBlocksMt( | 245 | fn processBlocksAsync( |
| 245 | blocks: *Blocks, | 246 | blocks: *Blocks, |
| 246 | time: u32, | 247 | time: u32, |
| 247 | memory: u32, | 248 | memory: u32, |
| ... | @@ -250,19 +251,20 @@ fn processBlocksMt( | ... | @@ -250,19 +251,20 @@ fn processBlocksMt( |
| 250 | lanes: u32, | 251 | lanes: u32, |
| 251 | segments: u32, | 252 | segments: u32, |
| 252 | io: Io, | 253 | io: Io, |
| 253 | ) void { | 254 | ) Io.Cancelable!void { |
| 254 | var n: u32 = 0; | 255 | var n: u32 = 0; |
| 255 | while (n < time) : (n += 1) { | 256 | while (n < time) : (n += 1) { |
| 256 | var slice: u32 = 0; | 257 | var slice: u32 = 0; |
| 257 | while (slice < sync_points) : (slice += 1) { | 258 | while (slice < sync_points) : (slice += 1) { |
| 258 | var group: Io.Group = .init; | 259 | var group: Io.Group = .init; |
| 260 | defer group.cancel(io); | ||
| 259 | var lane: u24 = 0; | 261 | var lane: u24 = 0; |
| 260 | while (lane < threads) : (lane += 1) { | 262 | while (lane < threads) : (lane += 1) { |
| 261 | group.async(io, processSegment, .{ | 263 | group.async(io, processSegment, .{ |
| 262 | blocks, time, memory, threads, mode, lanes, segments, n, slice, lane, | 264 | blocks, time, memory, threads, mode, lanes, segments, n, slice, lane, |
| 263 | }); | 265 | }); |
| 264 | } | 266 | } |
| 265 | group.wait(io); | 267 | try group.await(io); |
| 266 | } | 268 | } |
| 267 | } | 269 | } |
| 268 | } | 270 | } |
| ... | @@ -503,7 +505,7 @@ pub fn kdf( | ... | @@ -503,7 +505,7 @@ pub fn kdf( |
| 503 | blocks.appendNTimesAssumeCapacity(@splat(0), memory); | 505 | blocks.appendNTimesAssumeCapacity(@splat(0), memory); |
| 504 | 506 | ||
| 505 | initBlocks(&blocks, &h0, memory, params.p); | 507 | initBlocks(&blocks, &h0, memory, params.p); |
| 506 | processBlocks(&blocks, params.t, memory, params.p, mode, io); | 508 | try processBlocks(&blocks, params.t, memory, params.p, mode, io); |
| 507 | finalize(&blocks, memory, params.p, derived_key); | 509 | finalize(&blocks, memory, params.p, derived_key); |
| 508 | } | 510 | } |
| 509 | 511 |
lib/std/crypto/blake3.zig+10-6| ... | @@ -1,9 +1,11 @@ | ... | @@ -1,9 +1,11 @@ |
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | |||
| 3 | const std = @import("std"); | ||
| 3 | const fmt = std.fmt; | 4 | const fmt = std.fmt; |
| 4 | const mem = std.mem; | 5 | const mem = std.mem; |
| 5 | const Io = std.Io; | 6 | const Io = std.Io; |
| 6 | const Thread = std.Thread; | 7 | const Thread = std.Thread; |
| 8 | const Allocator = std.mem.Allocator; | ||
| 7 | 9 | ||
| 8 | const Vec4 = @Vector(4, u32); | 10 | const Vec4 = @Vector(4, u32); |
| 9 | const Vec8 = @Vector(8, u32); | 11 | const Vec8 = @Vector(8, u32); |
| ... | @@ -767,7 +769,7 @@ fn buildMerkleTreeLayerParallel( | ... | @@ -767,7 +769,7 @@ fn buildMerkleTreeLayerParallel( |
| 767 | key: [8]u32, | 769 | key: [8]u32, |
| 768 | flags: Flags, | 770 | flags: Flags, |
| 769 | io: Io, | 771 | io: Io, |
| 770 | ) void { | 772 | ) Io.Cancelable!void { |
| 771 | const num_parents = input_cvs.len / 2; | 773 | const num_parents = input_cvs.len / 2; |
| 772 | 774 | ||
| 773 | // Process sequentially with SIMD for smaller tree layers to avoid thread overhead | 775 | // Process sequentially with SIMD for smaller tree layers to avoid thread overhead |
| ... | @@ -787,6 +789,7 @@ fn buildMerkleTreeLayerParallel( | ... | @@ -787,6 +789,7 @@ fn buildMerkleTreeLayerParallel( |
| 787 | const num_workers = Thread.getCpuCount() catch 1; | 789 | const num_workers = Thread.getCpuCount() catch 1; |
| 788 | const parents_per_worker = (num_parents + num_workers - 1) / num_workers; | 790 | const parents_per_worker = (num_parents + num_workers - 1) / num_workers; |
| 789 | var group: Io.Group = .init; | 791 | var group: Io.Group = .init; |
| 792 | defer group.cancel(io); | ||
| 790 | 793 | ||
| 791 | for (0..num_workers) |worker_id| { | 794 | for (0..num_workers) |worker_id| { |
| 792 | const start_idx = worker_id * parents_per_worker; | 795 | const start_idx = worker_id * parents_per_worker; |
| ... | @@ -801,7 +804,7 @@ fn buildMerkleTreeLayerParallel( | ... | @@ -801,7 +804,7 @@ fn buildMerkleTreeLayerParallel( |
| 801 | .flags = flags, | 804 | .flags = flags, |
| 802 | }}); | 805 | }}); |
| 803 | } | 806 | } |
| 804 | group.wait(io); | 807 | try group.await(io); |
| 805 | } | 808 | } |
| 806 | 809 | ||
| 807 | fn parentOutput(parent_block: []const u8, key: [8]u32, flags: Flags) Output { | 810 | fn parentOutput(parent_block: []const u8, key: [8]u32, flags: Flags) Output { |
| ... | @@ -987,7 +990,7 @@ pub const Blake3 = struct { | ... | @@ -987,7 +990,7 @@ pub const Blake3 = struct { |
| 987 | d.final(out); | 990 | d.final(out); |
| 988 | } | 991 | } |
| 989 | 992 | ||
| 990 | pub fn hashParallel(b: []const u8, out: []u8, options: Options, allocator: std.mem.Allocator, io: Io) !void { | 993 | pub fn hashParallel(b: []const u8, out: []u8, options: Options, allocator: Allocator, io: Io) error{ OutOfMemory, Canceled }!void { |
| 991 | if (b.len < parallel_threshold) { | 994 | if (b.len < parallel_threshold) { |
| 992 | return hash(b, out, options); | 995 | return hash(b, out, options); |
| 993 | } | 996 | } |
| ... | @@ -1008,6 +1011,7 @@ pub const Blake3 = struct { | ... | @@ -1008,6 +1011,7 @@ pub const Blake3 = struct { |
| 1008 | const num_workers = thread_count; | 1011 | const num_workers = thread_count; |
| 1009 | const chunks_per_worker = (num_full_chunks + num_workers - 1) / num_workers; | 1012 | const chunks_per_worker = (num_full_chunks + num_workers - 1) / num_workers; |
| 1010 | var group: Io.Group = .init; | 1013 | var group: Io.Group = .init; |
| 1014 | defer group.cancel(io); | ||
| 1011 | 1015 | ||
| 1012 | for (0..num_workers) |worker_id| { | 1016 | for (0..num_workers) |worker_id| { |
| 1013 | const start_chunk = worker_id * chunks_per_worker; | 1017 | const start_chunk = worker_id * chunks_per_worker; |
| ... | @@ -1022,7 +1026,7 @@ pub const Blake3 = struct { | ... | @@ -1022,7 +1026,7 @@ pub const Blake3 = struct { |
| 1022 | .flags = flags, | 1026 | .flags = flags, |
| 1023 | }}); | 1027 | }}); |
| 1024 | } | 1028 | } |
| 1025 | group.wait(io); | 1029 | try group.await(io); |
| 1026 | 1030 | ||
| 1027 | // Build Merkle tree in parallel layers using ping-pong buffers | 1031 | // Build Merkle tree in parallel layers using ping-pong buffers |
| 1028 | const max_intermediate_size = (num_full_chunks + 1) / 2; | 1032 | const max_intermediate_size = (num_full_chunks + 1) / 2; |
| ... | @@ -1040,7 +1044,7 @@ pub const Blake3 = struct { | ... | @@ -1040,7 +1044,7 @@ pub const Blake3 = struct { |
| 1040 | const has_odd = current_level.len % 2 == 1; | 1044 | const has_odd = current_level.len % 2 == 1; |
| 1041 | const next_level_size = num_parents + @intFromBool(has_odd); | 1045 | const next_level_size = num_parents + @intFromBool(has_odd); |
| 1042 | 1046 | ||
| 1043 | buildMerkleTreeLayerParallel( | 1047 | try buildMerkleTreeLayerParallel( |
| 1044 | current_level[0 .. num_parents * 2], | 1048 | current_level[0 .. num_parents * 2], |
| 1045 | next_level_buf[0..num_parents], | 1049 | next_level_buf[0..num_parents], |
| 1046 | key_words, | 1050 | key_words, |
lib/std/crypto/kangarootwelve.zig+9-7| ... | @@ -1,9 +1,10 @@ | ... | @@ -1,9 +1,10 @@ |
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | |||
| 3 | const std = @import("std"); | ||
| 3 | const crypto = std.crypto; | 4 | const crypto = std.crypto; |
| 4 | const Allocator = std.mem.Allocator; | 5 | const Allocator = std.mem.Allocator; |
| 5 | const Io = std.Io; | 6 | const Io = std.Io; |
| 6 | const Thread = std.Thread; | 7 | const assert = std.debug.assert; |
| 7 | 8 | ||
| 8 | const TurboSHAKE128State = crypto.hash.sha3.TurboShake128(0x06); | 9 | const TurboSHAKE128State = crypto.hash.sha3.TurboShake128(0x06); |
| 9 | const TurboSHAKE256State = crypto.hash.sha3.TurboShake256(0x06); | 10 | const TurboSHAKE256State = crypto.hash.sha3.TurboShake256(0x06); |
| ... | @@ -598,7 +599,7 @@ inline fn processNLeaves( | ... | @@ -598,7 +599,7 @@ inline fn processNLeaves( |
| 598 | output: []align(@alignOf(u64)) u8, | 599 | output: []align(@alignOf(u64)) u8, |
| 599 | ) void { | 600 | ) void { |
| 600 | const cv_size = Variant.cv_size; | 601 | const cv_size = Variant.cv_size; |
| 601 | comptime std.debug.assert(cv_size % @sizeOf(u64) == 0); | 602 | comptime assert(cv_size % @sizeOf(u64) == 0); |
| 602 | 603 | ||
| 603 | if (view.tryGetSlice(j, j + N * chunk_size)) |leaf_data| { | 604 | if (view.tryGetSlice(j, j + N * chunk_size)) |leaf_data| { |
| 604 | var leaf_cvs: [N * cv_size]u8 = undefined; | 605 | var leaf_cvs: [N * cv_size]u8 = undefined; |
| ... | @@ -645,7 +646,7 @@ fn processLeafBatch(comptime Variant: type, ctx: LeafBatchContext) void { | ... | @@ -645,7 +646,7 @@ fn processLeafBatch(comptime Variant: type, ctx: LeafBatchContext) void { |
| 645 | j += chunk_len; | 646 | j += chunk_len; |
| 646 | } | 647 | } |
| 647 | 648 | ||
| 648 | std.debug.assert(cvs_offset == ctx.output_cvs.len); | 649 | assert(cvs_offset == ctx.output_cvs.len); |
| 649 | } | 650 | } |
| 650 | 651 | ||
| 651 | /// Helper to process N leaves in SIMD and absorb CVs into state | 652 | /// Helper to process N leaves in SIMD and absorb CVs into state |
| ... | @@ -841,7 +842,7 @@ fn ktMultiThreaded( | ... | @@ -841,7 +842,7 @@ fn ktMultiThreaded( |
| 841 | total_len: usize, | 842 | total_len: usize, |
| 842 | output: []u8, | 843 | output: []u8, |
| 843 | ) !void { | 844 | ) !void { |
| 844 | comptime std.debug.assert(bytes_per_batch % (optimal_vector_len * chunk_size) == 0); | 845 | comptime assert(bytes_per_batch % (optimal_vector_len * chunk_size) == 0); |
| 845 | 846 | ||
| 846 | const cv_size = Variant.cv_size; | 847 | const cv_size = Variant.cv_size; |
| 847 | const StateType = Variant.StateType; | 848 | const StateType = Variant.StateType; |
| ... | @@ -883,6 +884,7 @@ fn ktMultiThreaded( | ... | @@ -883,6 +884,7 @@ fn ktMultiThreaded( |
| 883 | var pending_cv_lens: [256]usize = .{0} ** 256; | 884 | var pending_cv_lens: [256]usize = .{0} ** 256; |
| 884 | 885 | ||
| 885 | var select: Select = .init(io, select_buf); | 886 | var select: Select = .init(io, select_buf); |
| 887 | defer select.cancel(); | ||
| 886 | var batches_spawned: usize = 0; | 888 | var batches_spawned: usize = 0; |
| 887 | var next_to_process: usize = 0; | 889 | var next_to_process: usize = 0; |
| 888 | 890 | ||
| ... | @@ -901,7 +903,7 @@ fn ktMultiThreaded( | ... | @@ -901,7 +903,7 @@ fn ktMultiThreaded( |
| 901 | batches_spawned += 1; | 903 | batches_spawned += 1; |
| 902 | } | 904 | } |
| 903 | 905 | ||
| 904 | const result = select.wait() catch unreachable; | 906 | const result = try select.await(); |
| 905 | const batch = result.batch; | 907 | const batch = result.batch; |
| 906 | const slot = batch.batch_idx % max_concurrent; | 908 | const slot = batch.batch_idx % max_concurrent; |
| 907 | 909 | ||
| ... | @@ -925,7 +927,7 @@ fn ktMultiThreaded( | ... | @@ -925,7 +927,7 @@ fn ktMultiThreaded( |
| 925 | } | 927 | } |
| 926 | } | 928 | } |
| 927 | 929 | ||
| 928 | select.group.wait(io); | 930 | assert(select.outstanding == 0); |
| 929 | } | 931 | } |
| 930 | 932 | ||
| 931 | if (has_partial_leaf) { | 933 | if (has_partial_leaf) { |
src/Compilation.zig+12-6| ... | @@ -4698,7 +4698,7 @@ fn performAllTheWork( | ... | @@ -4698,7 +4698,7 @@ fn performAllTheWork( |
| 4698 | }); | 4698 | }); |
| 4699 | } | 4699 | } |
| 4700 | 4700 | ||
| 4701 | astgen_group.wait(io); | 4701 | try astgen_group.await(io); |
| 4702 | } | 4702 | } |
| 4703 | 4703 | ||
| 4704 | if (comp.zcu) |zcu| { | 4704 | if (comp.zcu) |zcu| { |
| ... | @@ -4761,7 +4761,7 @@ fn performAllTheWork( | ... | @@ -4761,7 +4761,7 @@ fn performAllTheWork( |
| 4761 | // Since we're skipping analysis, there are no ZCU link tasks. | 4761 | // Since we're skipping analysis, there are no ZCU link tasks. |
| 4762 | comp.link_queue.finishZcuQueue(comp); | 4762 | comp.link_queue.finishZcuQueue(comp); |
| 4763 | // Let other compilation work finish to collect as many errors as possible. | 4763 | // Let other compilation work finish to collect as many errors as possible. |
| 4764 | misc_group.wait(io); | 4764 | try misc_group.await(io); |
| 4765 | comp.link_queue.wait(io); | 4765 | comp.link_queue.wait(io); |
| 4766 | return; | 4766 | return; |
| 4767 | } | 4767 | } |
| ... | @@ -4850,18 +4850,22 @@ fn performAllTheWork( | ... | @@ -4850,18 +4850,22 @@ fn performAllTheWork( |
| 4850 | comp.link_queue.finishZcuQueue(comp); | 4850 | comp.link_queue.finishZcuQueue(comp); |
| 4851 | 4851 | ||
| 4852 | // Main thread work is all done, now just wait for all async work. | 4852 | // Main thread work is all done, now just wait for all async work. |
| 4853 | misc_group.wait(io); | 4853 | try misc_group.await(io); |
| 4854 | comp.link_queue.wait(io); | 4854 | comp.link_queue.wait(io); |
| 4855 | } | 4855 | } |
| 4856 | 4856 | ||
| 4857 | fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node) void { | 4857 | fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node) void { |
| 4858 | const io = comp.io; | 4858 | const io = comp.io; |
| 4859 | 4859 | ||
| 4860 | // TODO should this function be cancelable? | ||
| 4861 | const prev_cancel_prot = io.swapCancelProtection(.blocked); | ||
| 4862 | defer _ = io.swapCancelProtection(prev_cancel_prot); | ||
| 4863 | |||
| 4860 | var prelink_group: Io.Group = .init; | 4864 | var prelink_group: Io.Group = .init; |
| 4861 | defer prelink_group.cancel(io); | 4865 | defer prelink_group.cancel(io); |
| 4862 | 4866 | ||
| 4863 | comp.queuePrelinkTasks(comp.oneshot_prelink_tasks.items) catch |err| switch (err) { | 4867 | comp.queuePrelinkTasks(comp.oneshot_prelink_tasks.items) catch |err| switch (err) { |
| 4864 | error.Canceled => return, | 4868 | error.Canceled => unreachable, // see swapCancelProtection above |
| 4865 | }; | 4869 | }; |
| 4866 | comp.oneshot_prelink_tasks.clearRetainingCapacity(); | 4870 | comp.oneshot_prelink_tasks.clearRetainingCapacity(); |
| 4867 | 4871 | ||
| ... | @@ -5055,9 +5059,11 @@ fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node | ... | @@ -5055,9 +5059,11 @@ fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node |
| 5055 | }); | 5059 | }); |
| 5056 | } | 5060 | } |
| 5057 | 5061 | ||
| 5058 | prelink_group.wait(io); | 5062 | prelink_group.await(io) catch |err| switch (err) { |
| 5063 | error.Canceled => unreachable, // see swapCancelProtection above | ||
| 5064 | }; | ||
| 5059 | comp.link_queue.finishPrelinkQueue(comp) catch |err| switch (err) { | 5065 | comp.link_queue.finishPrelinkQueue(comp) catch |err| switch (err) { |
| 5060 | error.Canceled => return, | 5066 | error.Canceled => unreachable, // see swapCancelProtection above |
| 5061 | }; | 5067 | }; |
| 5062 | } | 5068 | } |
| 5063 | 5069 |
src/Package/Fetch.zig+10-6| ... | @@ -146,6 +146,8 @@ pub const JobQueue = struct { | ... | @@ -146,6 +146,8 @@ pub const JobQueue = struct { |
| 146 | pub const UnlazySet = std.AutoArrayHashMapUnmanaged(Package.Hash, void); | 146 | pub const UnlazySet = std.AutoArrayHashMapUnmanaged(Package.Hash, void); |
| 147 | 147 | ||
| 148 | pub fn deinit(jq: *JobQueue) void { | 148 | pub fn deinit(jq: *JobQueue) void { |
| 149 | const io = jq.io; | ||
| 150 | jq.group.cancel(io); | ||
| 149 | if (jq.all_fetches.items.len == 0) return; | 151 | if (jq.all_fetches.items.len == 0) return; |
| 150 | const gpa = jq.all_fetches.items[0].arena.child_allocator; | 152 | const gpa = jq.all_fetches.items[0].arena.child_allocator; |
| 151 | jq.table.deinit(gpa); | 153 | jq.table.deinit(gpa); |
| ... | @@ -847,7 +849,7 @@ pub fn workerRun(f: *Fetch, prog_name: []const u8) void { | ... | @@ -847,7 +849,7 @@ pub fn workerRun(f: *Fetch, prog_name: []const u8) void { |
| 847 | 849 | ||
| 848 | run(f) catch |err| switch (err) { | 850 | run(f) catch |err| switch (err) { |
| 849 | error.OutOfMemory => f.oom_flag = true, | 851 | error.OutOfMemory => f.oom_flag = true, |
| 850 | error.Canceled => {}, | 852 | error.Canceled => {}, // TODO make groupAsync functions be cancelable and assert proper value was returned |
| 851 | error.FetchFailed => { | 853 | error.FetchFailed => { |
| 852 | // Nothing to do because the errors are already reported in `error_bundle`, | 854 | // Nothing to do because the errors are already reported in `error_bundle`, |
| 853 | // and a reference is kept to the `Fetch` task inside `all_fetches`. | 855 | // and a reference is kept to the `Fetch` task inside `all_fetches`. |
| ... | @@ -1517,12 +1519,12 @@ fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!Compute | ... | @@ -1517,12 +1519,12 @@ fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!Compute |
| 1517 | // The final hash will be a hash of each file hashed independently. This | 1519 | // The final hash will be a hash of each file hashed independently. This |
| 1518 | // allows hashing in parallel. | 1520 | // allows hashing in parallel. |
| 1519 | var group: Io.Group = .init; | 1521 | var group: Io.Group = .init; |
| 1520 | defer group.wait(io); | 1522 | defer group.cancel(io); |
| 1521 | 1523 | ||
| 1522 | while (walker.next(io) catch |err| { | 1524 | while (walker.next(io) catch |err| { |
| 1523 | try eb.addRootErrorMessage(.{ .msg = try eb.printString( | 1525 | try eb.addRootErrorMessage(.{ .msg = try eb.printString( |
| 1524 | "unable to walk temporary directory '{f}': {s}", | 1526 | "unable to walk temporary directory '{f}': {t}", |
| 1525 | .{ pkg_path, @errorName(err) }, | 1527 | .{ pkg_path, err }, |
| 1526 | ) }); | 1528 | ) }); |
| 1527 | return error.FetchFailed; | 1529 | return error.FetchFailed; |
| 1528 | }) |entry| { | 1530 | }) |entry| { |
| ... | @@ -1552,8 +1554,8 @@ fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!Compute | ... | @@ -1552,8 +1554,8 @@ fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!Compute |
| 1552 | .file => .file, | 1554 | .file => .file, |
| 1553 | .sym_link => .link, | 1555 | .sym_link => .link, |
| 1554 | else => return f.fail(f.location_tok, try eb.printString( | 1556 | else => return f.fail(f.location_tok, try eb.printString( |
| 1555 | "package contains '{s}' which has illegal file type '{s}'", | 1557 | "package contains '{s}' which has illegal file type '{t}'", |
| 1556 | .{ entry.path, @tagName(entry.kind) }, | 1558 | .{ entry.path, entry.kind }, |
| 1557 | )), | 1559 | )), |
| 1558 | }; | 1560 | }; |
| 1559 | 1561 | ||
| ... | @@ -1573,6 +1575,8 @@ fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!Compute | ... | @@ -1573,6 +1575,8 @@ fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!Compute |
| 1573 | group.async(io, workerHashFile, .{ io, root_dir, hashed_file }); | 1575 | group.async(io, workerHashFile, .{ io, root_dir, hashed_file }); |
| 1574 | try all_files.append(hashed_file); | 1576 | try all_files.append(hashed_file); |
| 1575 | } | 1577 | } |
| 1578 | |||
| 1579 | try group.await(io); | ||
| 1576 | } | 1580 | } |
| 1577 | 1581 | ||
| 1578 | { | 1582 | { |
src/link/MachO/hasher.zig+1-1| ... | @@ -48,7 +48,7 @@ pub fn ParallelHasher(comptime Hasher: type) type { | ... | @@ -48,7 +48,7 @@ pub fn ParallelHasher(comptime Hasher: type) type { |
| 48 | }); | 48 | }); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | group.wait(io); | 51 | try group.await(io); |
| 52 | } | 52 | } |
| 53 | for (results) |result| _ = try result; | 53 | for (results) |result| _ = try result; |
| 54 | } | 54 | } |
src/main.zig+1-1| ... | @@ -5284,7 +5284,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) | ... | @@ -5284,7 +5284,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) |
| 5284 | ); | 5284 | ); |
| 5285 | 5285 | ||
| 5286 | job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" }); | 5286 | job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" }); |
| 5287 | job_queue.group.wait(io); | 5287 | try job_queue.group.await(io); |
| 5288 | 5288 | ||
| 5289 | try job_queue.consolidateErrors(); | 5289 | try job_queue.consolidateErrors(); |
| 5290 | 5290 |
tools/update_cpu_features.zig+1-1| ... | @@ -1951,7 +1951,7 @@ pub fn main() anyerror!void { | ... | @@ -1951,7 +1951,7 @@ pub fn main() anyerror!void { |
| 1951 | } }); | 1951 | } }); |
| 1952 | } | 1952 | } |
| 1953 | 1953 | ||
| 1954 | group.wait(io); | 1954 | try group.await(io); |
| 1955 | } | 1955 | } |
| 1956 | 1956 | ||
| 1957 | const Job = struct { | 1957 | const Job = struct { |