authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-22 14:32:39-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-27 15:32:34-08:00
loge0d9c04e35bf122b362697b543f5fb917a561182
tree8a814fca88f1b1ebecc9d306109205442df3cc49
parentc155ac50c25c871ea80f27688d2361a3a0bc24d6

std.Io.Threaded: avoid extra fields of Thread

As mlugg pointed out those race when a thread finishes an operation just after it is canceled and then that thread to picks up another task, resulting in these fields being potentially overwritten. This updates fileReadStreaming on Windows to handle being alerted, and then manage its own cancelation of the file I/O.

2 files changed, 103 insertions(+), 174 deletions(-)

lib/std/Io/Threaded.zig+102-173
......@@ -339,8 +339,8 @@ const Group = struct {
339339 .canceled => true,
340340 .parked => unreachable,
341341 .blocked => unreachable,
342 .blocked_apc => unreachable,
343 .blocked_windows_dns => unreachable,
342 .blocked_alertable => unreachable,
343 .blocked_alertable_canceling => unreachable,
344344 .blocked_canceling => unreachable,
345345 };
346346 if (result) {
......@@ -379,10 +379,7 @@ const Group = struct {
379379 while (it) |thread| : (it = thread.next) {
380380 // This non-mutating RMW exists for ordering reasons: see comment in `Group.Task.start` for reasons.
381381 _ = thread.status.fetchOr(.{ .cancelation = @enumFromInt(0), .awaitable = .null }, .release);
382 if (thread.cancelAwaitable(.fromGroup(g.ptr))) |method| {
383 thread.interrupt_method = method;
384 any_blocked = true;
385 }
382 if (thread.cancelAwaitable(.fromGroup(g.ptr))) any_blocked = true;
386383 }
387384 return any_blocked;
388385 }
......@@ -394,7 +391,7 @@ const Group = struct {
394391 var any_signaled = false;
395392 var it = t.worker_threads.load(.acquire); // acquire `Thread` values
396393 while (it) |thread| : (it = thread.next) {
397 if (thread.signalCanceledSyscall(t, .fromGroup(g.ptr), thread.interrupt_method)) any_signaled = true;
394 if (thread.signalCanceledSyscall(t, .fromGroup(g.ptr))) any_signaled = true;
398395 }
399396 return any_signaled;
400397 }
......@@ -546,8 +543,8 @@ const Future = struct {
546543 .canceled => true,
547544 .parked => unreachable,
548545 .blocked => unreachable,
549 .blocked_apc => unreachable,
550 .blocked_windows_dns => unreachable,
546 .blocked_alertable => unreachable,
547 .blocked_alertable_canceling => unreachable,
551548 .blocked_canceling => unreachable,
552549 };
553550 thread.status.store(.{ .cancelation = .none, .awaitable = .null }, .monotonic);
......@@ -576,15 +573,11 @@ const Future = struct {
576573 num_completed: *std.atomic.Value(u32),
577574 thread: ?*Thread,
578575 ) void {
579 var interrupt_method: ?Thread.InterruptMethod =
580 if (thread) |th| th.cancelAwaitable(.fromFuture(future)) else null;
576 var need_signal: bool = if (thread) |th| th.cancelAwaitable(.fromFuture(future)) else false;
581577 var timeout_ns: u64 = 1 << 10;
582578 while (true) {
583 if (interrupt_method) |method| {
584 if (!thread.?.signalCanceledSyscall(t, .fromFuture(future), method))
585 interrupt_method = null;
586 }
587 Thread.futexWaitUncancelable(&num_completed.raw, 0, if (interrupt_method != null) timeout_ns else null);
579 need_signal = need_signal and thread.?.signalCanceledSyscall(t, .fromFuture(future));
580 Thread.futexWaitUncancelable(&num_completed.raw, 0, if (need_signal) timeout_ns else null);
588581 switch (num_completed.load(.acquire)) { // acquire task results
589582 0 => {},
590583 1 => break,
......@@ -632,17 +625,9 @@ const Thread = struct {
632625 cancel_protection: Io.CancelProtection,
633626 /// Always released when `Status.cancelation` is set to `.parked`.
634627 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,
635 apc: Apc,
636 /// Used only by group cancelation code for temporary storage.
637 interrupt_method: InterruptMethod,
638628
639629 csprng: Csprng,
640630
641 const Apc = if (is_windows) struct {
642 handle: windows.HANDLE,
643 iosb: ?*windows.IO_STATUS_BLOCK,
644 } else void;
645
646631 const Handle = Handle: {
647632 if (std.Thread.use_pthreads) break :Handle std.c.pthread_t;
648633 if (is_windows) break :Handle windows.HANDLE;
......@@ -667,13 +652,11 @@ const Thread = struct {
667652 /// To request cancelation, set the status to `.blocked_canceling` and repeatedly interrupt the system call until the status changes.
668653 blocked = 0b011,
669654
670 /// Windows-only: the thread is blocked in a call to `NtDelayExecution`.
671 /// To request cancelation, set the status to `.canceling` and call `NtCancelIoFileEx`.
672 blocked_apc = 0b100,
673
674 /// Windows-only: the thread is blocked in a call to `GetAddrInfoExW`.
675 /// To request cancelation, set the status to `.canceling` and call `GetAddrInfoExCancel`.
676 blocked_windows_dns = 0b010,
655 /// Windows-only: the thread is blocked in an alertable wait via
656 /// `NtDelayExecution`. To request cancelation, set the status to
657 /// `blocked_alertable_canceling` and repeatedly alert the thread
658 /// until the status changes.
659 blocked_alertable = 0b010,
677660
678661 /// The thread has an outstanding cancelation request but is not in a cancelable operation.
679662 /// When it acknowledges the cancelation, it will set the status to `.canceled`.
......@@ -722,8 +705,8 @@ const Thread = struct {
722705 switch (status.cancelation) {
723706 .parked => unreachable,
724707 .blocked => unreachable,
725 .blocked_apc => unreachable,
726 .blocked_windows_dns => unreachable,
708 .blocked_alertable => unreachable,
709 .blocked_alertable_canceling => unreachable,
727710 .blocked_canceling => unreachable,
728711 .none, .canceled => {},
729712 .canceling => {
......@@ -1003,17 +986,17 @@ const Thread = struct {
1003986 /// It is possible that `thread` gets canceled by this function, but is blocked in a syscall. In
1004987 /// that case, the thread may need to be sent a signal to interrupt the call. This function will
1005988 /// return `true` to indicate this, in which case the caller must call `signalCanceledSyscall`.
1006 fn cancelAwaitable(thread: *Thread, awaitable: AwaitableId) ?InterruptMethod {
989 fn cancelAwaitable(thread: *Thread, awaitable: AwaitableId) bool {
1007990 var status = thread.status.load(.monotonic);
1008991 while (true) {
1009 if (status.awaitable != awaitable) return null; // thread is working on something else
992 if (status.awaitable != awaitable) return false; // thread is working on something else
1010993 status = switch (status.cancelation) {
1011994 .none => thread.status.cmpxchgWeak(
1012995 .{ .cancelation = .none, .awaitable = awaitable },
1013996 .{ .cancelation = .canceling, .awaitable = awaitable },
1014997 .monotonic,
1015998 .monotonic,
1016 ) orelse return null,
999 ) orelse return false,
10171000
10181001 .parked => thread.status.cmpxchgWeak(
10191002 .{ .cancelation = .parked, .awaitable = awaitable },
......@@ -1026,7 +1009,7 @@ const Thread = struct {
10261009 parking_futex.removeCanceledWaiter(futex_waiter);
10271010 }
10281011 unpark(&.{thread.id}, null);
1029 return null;
1012 return false;
10301013 },
10311014
10321015 .blocked => thread.status.cmpxchgWeak(
......@@ -1034,17 +1017,7 @@ const Thread = struct {
10341017 .{ .cancelation = .blocked_canceling, .awaitable = awaitable },
10351018 .monotonic,
10361019 .monotonic,
1037 ) orelse return .sync,
1038
1039 .blocked_apc => thread.status.cmpxchgWeak(
1040 .{ .cancelation = .blocked_apc, .awaitable = awaitable },
1041 .{ .cancelation = .canceling, .awaitable = awaitable },
1042 .monotonic,
1043 .monotonic,
1044 ) orelse {
1045 if (!is_windows) unreachable;
1046 return .apc;
1047 },
1020 ) orelse return true,
10481021
10491022 .blocked_alertable => thread.status.cmpxchgWeak(
10501023 .{ .cancelation = .blocked_alertable, .awaitable = awaitable },
......@@ -1053,14 +1026,14 @@ const Thread = struct {
10531026 .monotonic,
10541027 ) orelse {
10551028 if (!is_windows) unreachable;
1056 return .dns;
1029 return true;
10571030 },
10581031
10591032 .canceling, .canceled => {
10601033 // This can happen when the task start raced with the cancelation, so the thread
10611034 // saw the cancelation on the future/group *and* we are trying to signal the
10621035 // thread here.
1063 return null;
1036 return false;
10641037 },
10651038
10661039 .blocked_canceling => unreachable, // `awaitable` has not been canceled before now
......@@ -1069,11 +1042,6 @@ const Thread = struct {
10691042 }
10701043 }
10711044
1072 const InterruptMethod = switch (native_os) {
1073 .windows => enum { sync, dns, apc },
1074 else => enum { sync },
1075 };
1076
10771045 /// Sends a signal to `thread` if it is still blocked in a syscall (i.e. has not yet observed
10781046 /// the cancelation request from `cancelAwaitable`).
10791047 ///
......@@ -1083,21 +1051,24 @@ const Thread = struct {
10831051 /// the thread is still blocked. For the implementation, `Future.waitForCancelWithSignaling` and
10841052 /// `Group.waitForCancelWithSignaling`: they use exponential backoff starting at a 1us delay and
10851053 /// doubling each call. In practice, it is rare to send more than one signal.
1086 fn signalCanceledSyscall(thread: *Thread, t: *Threaded, awaitable: AwaitableId, method: InterruptMethod) bool {
1087 const bad_status: Status = .{ .cancelation = .blocked_canceling, .awaitable = awaitable };
1088 if (thread.status.load(.monotonic) != bad_status) return false;
1054 fn signalCanceledSyscall(thread: *Thread, t: *Threaded, awaitable: AwaitableId) bool {
1055 const status = thread.status.load(.monotonic);
1056 if (status.awaitable != awaitable) {
1057 // The thread has moved on and is working on something totally different.
1058 return false;
1059 }
10891060
10901061 // The thread ID and/or handle can be read non-atomically because they never change and were
10911062 // released by the store that made `thread` available to us.
10921063
1093 if (std.Thread.use_pthreads) switch (method) {
1094 .sync => return switch (std.c.pthread_kill(thread.handle, .IO)) {
1095 0 => true,
1096 else => false,
1097 },
1098 } else switch (native_os) {
1099 .linux => switch (method) {
1100 .sync => {
1064 switch (status.cancelation) {
1065 .blocked_canceling => if (std.Thread.use_pthreads) {
1066 return switch (std.c.pthread_kill(thread.handle, .IO)) {
1067 0 => true,
1068 else => false,
1069 };
1070 } else switch (native_os) {
1071 .linux => {
11011072 const pid: posix.pid_t = pid: {
11021073 const cached_pid = @atomicLoad(Pid, &t.pid, .monotonic);
11031074 if (cached_pid != .unknown) break :pid @intFromEnum(cached_pid);
......@@ -1110,9 +1081,7 @@ const Thread = struct {
11101081 else => false,
11111082 };
11121083 },
1113 },
1114 .windows => switch (method) {
1115 .sync => {
1084 .windows => {
11161085 var iosb: windows.IO_STATUS_BLOCK = undefined;
11171086 return switch (windows.ntdll.NtCancelSynchronousIoFile(thread.handle, null, &iosb)) {
11181087 .NOT_FOUND => true, // this might mean the operation hasn't started yet
......@@ -1120,15 +1089,15 @@ const Thread = struct {
11201089 else => false,
11211090 };
11221091 },
1123 .dns => @panic("TODO call GetAddrInfoExCancel"),
1124 .apc => {
1125 var iosb: windows.IO_STATUS_BLOCK = undefined;
1126 return switch (windows.ntdll.NtCancelIoFileEx(thread.apc.handle, thread.apc.iosb, &iosb)) {
1127 .NOT_FOUND => true, // this might mean the operation hasn't started yet
1128 .SUCCESS => false, // the OS confirmed that our cancelation worked
1129 else => false,
1130 };
1131 },
1092 else => return false,
1093 },
1094
1095 .blocked_alertable_canceling => {
1096 if (!is_windows) unreachable;
1097 return switch (windows.ntdll.NtAlertThread(thread.handle)) {
1098 .SUCCESS => true,
1099 else => false,
1100 };
11321101 },
11331102
11341103 else => {
......@@ -1176,8 +1145,8 @@ const Syscall = struct {
11761145 }, .monotonic).cancelation) {
11771146 .parked => unreachable,
11781147 .blocked => unreachable,
1179 .blocked_apc => unreachable,
1180 .blocked_windows_dns => unreachable,
1148 .blocked_alertable => unreachable,
1149 .blocked_alertable_canceling => unreachable,
11811150 .blocked_canceling => unreachable,
11821151 .none => return .{ .thread = thread }, // new status is `.blocked`
11831152 .canceling => return error.Canceled, // new status is `.canceled`
......@@ -1196,8 +1165,8 @@ const Syscall = struct {
11961165 }, .monotonic).cancelation) {
11971166 .none => unreachable,
11981167 .parked => unreachable,
1199 .blocked_apc => unreachable,
1200 .blocked_windows_dns => unreachable,
1168 .blocked_alertable => unreachable,
1169 .blocked_alertable_canceling => unreachable,
12011170 .canceling => unreachable,
12021171 .canceled => unreachable,
12031172 .blocked => {}, // new status is `.blocked` (unchanged)
......@@ -1213,8 +1182,8 @@ const Syscall = struct {
12131182 }, .monotonic).cancelation) {
12141183 .none => unreachable,
12151184 .parked => unreachable,
1216 .blocked_apc => unreachable,
1217 .blocked_windows_dns => unreachable,
1185 .blocked_alertable => unreachable,
1186 .blocked_alertable_canceling => unreachable,
12181187 .canceling => unreachable,
12191188 .canceled => unreachable,
12201189 .blocked => {}, // new status is `.none`
......@@ -1222,25 +1191,25 @@ const Syscall = struct {
12221191 }
12231192 }
12241193 /// Indicates instead of `NtCancelSynchronousIoFile` we need to use
1225 /// `NtCancelIoFileEx` to interrupt the wait.
1194 /// `NtAlertThread` to interrupt the wait.
12261195 ///
12271196 /// Windows only, called from blocked state only.
1228 fn toApc(s: Syscall, apc: Thread.Apc) Io.Cancelable!void {
1229 const thread = s.thread orelse return;
1230 thread.apc = apc;
1197 fn toAlertable(s: Syscall) Io.Cancelable!AlertableSyscall {
1198 comptime assert(is_windows);
1199 const thread = s.thread orelse return .{ .thread = null };
12311200 var prev = thread.status.load(.monotonic);
12321201 while (true) prev = switch (prev.cancelation) {
12331202 .none => unreachable,
12341203 .parked => unreachable,
1235 .blocked_apc => unreachable,
1236 .blocked_windows_dns => unreachable,
1204 .blocked_alertable => unreachable,
1205 .blocked_alertable_canceling => unreachable,
12371206 .canceling => unreachable,
12381207 .canceled => unreachable,
12391208
12401209 .blocked => thread.status.cmpxchgWeak(prev, .{
1241 .cancelation = .blocked_apc,
1210 .cancelation = .blocked_alertable,
12421211 .awaitable = prev.awaitable,
1243 }, .monotonic, .monotonic) orelse return,
1212 }, .monotonic, .monotonic) orelse return .{ .thread = thread },
12441213
12451214 .blocked_canceling => thread.status.cmpxchgWeak(prev, .{
12461215 .cancelation = .canceled,
......@@ -1248,45 +1217,6 @@ const Syscall = struct {
12481217 }, .monotonic, .monotonic) orelse return error.Canceled,
12491218 };
12501219 }
1251 /// Windows only, called from blocked_apc state only.
1252 fn checkCancelApc(s: Syscall) Io.Cancelable!void {
1253 const thread = s.thread orelse return;
1254 var prev = thread.status.load(.monotonic);
1255 while (true) prev = switch (prev.cancelation) {
1256 .none => unreachable,
1257 .parked => unreachable,
1258 .blocked_windows_dns => unreachable,
1259 .blocked => unreachable,
1260 .canceling => unreachable,
1261 .canceled => unreachable,
1262 .blocked_apc => return,
1263 .blocked_canceling => thread.status.cmpxchgWeak(prev, .{
1264 .cancelation = .canceled,
1265 .awaitable = prev.awaitable,
1266 }, .monotonic, .monotonic) orelse return error.Canceled,
1267 };
1268 }
1269 /// Windows only, called from blocked_apc state only.
1270 fn finishApc(s: Syscall) void {
1271 const thread = s.thread orelse return;
1272 var prev = thread.status.load(.monotonic);
1273 while (true) prev = switch (prev.cancelation) {
1274 .none => unreachable,
1275 .parked => unreachable,
1276 .blocked_windows_dns => unreachable,
1277 .blocked => unreachable,
1278 .canceling => unreachable,
1279 .canceled => unreachable,
1280 .blocked_apc => thread.status.cmpxchgWeak(prev, .{
1281 .cancelation = .none,
1282 .awaitable = prev.awaitable,
1283 }, .monotonic, .monotonic) orelse return,
1284 .blocked_canceling => thread.status.cmpxchgWeak(prev, .{
1285 .cancelation = .canceling,
1286 .awaitable = prev.awaitable,
1287 }, .monotonic, .monotonic) orelse return,
1288 };
1289 }
12901220 /// Convenience wrapper which calls `finish`, then returns `err`.
12911221 fn fail(s: Syscall, err: anytype) @TypeOf(err) {
12921222 s.finish();
......@@ -1566,8 +1496,6 @@ fn worker(t: *Threaded) void {
15661496 .cancel_protection = .unblocked,
15671497 .futex_waiter = undefined,
15681498 .csprng = .{},
1569 .apc = undefined,
1570 .interrupt_method = undefined,
15711499 };
15721500 Thread.current = &thread;
15731501
......@@ -2172,8 +2100,8 @@ fn groupAsyncEager(
21722100 .canceled => true,
21732101 .parked => unreachable,
21742102 .blocked => unreachable,
2175 .blocked_apc => unreachable,
2176 .blocked_windows_dns => unreachable,
2103 .blocked_alertable => unreachable,
2104 .blocked_alertable_canceling => unreachable,
21772105 .blocked_canceling => unreachable,
21782106 };
21792107 } else false;
......@@ -2184,8 +2112,8 @@ fn groupAsyncEager(
21842112 .canceled => true,
21852113 .parked => unreachable,
21862114 .blocked => unreachable,
2187 .blocked_apc => unreachable,
2188 .blocked_windows_dns => unreachable,
2115 .blocked_alertable => unreachable,
2116 .blocked_alertable_canceling => unreachable,
21892117 .blocked_canceling => unreachable,
21902118 };
21912119 } else false;
......@@ -2364,8 +2292,8 @@ fn recancelInner() void {
23642292 .canceling => unreachable, // called `recancel` but cancelation was already pending
23652293 .parked => unreachable,
23662294 .blocked => unreachable,
2367 .blocked_apc => unreachable,
2368 .blocked_windows_dns => unreachable,
2295 .blocked_alertable => unreachable,
2296 .blocked_alertable_canceling => unreachable,
23692297 .blocked_canceling => unreachable,
23702298 }
23712299}
......@@ -8324,36 +8252,37 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us
83248252 continue;
83258253 },
83268254 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err), // streaming read of async mode file
8327 else => |status| std.debug.panic("fileReadStreamingWindows NtReadFile returned {t}", .{status}),
8328 //else => |status| return syscall.unexpectedNtstatus(status),
8255 else => |status| return syscall.unexpectedNtstatus(status),
83298256 }
83308257 }
8331 try syscall.toApc(.{ .handle = file.handle, .iosb = &io_status_block });
8332 while (true) {
8333 switch (windows.ntdll.NtDelayExecution(1, &infinite)) {
8334 .USER_APC => {
8335 if (!done) {
8336 // Other APC work was queued before calling into this function.
8337 try syscall.checkCancelApc();
8338 continue;
8339 }
8340 break syscall.finishApc();
8341 },
8342 .SUCCESS, .CANCELLED, .TIMEOUT, .ALERTED => {
8343 try syscall.checkCancelApc();
8344 continue;
8258 // Once we get here we received PENDING so we must not return from the
8259 // function until the operation completes.
8260 defer while (!done) {
8261 _ = windows.ntdll.NtDelayExecution(1, &infinite);
8262 };
8263
8264 const alertable_syscall = syscall.toAlertable() catch |err| switch (err) {
8265 error.Canceled => |e| {
8266 _ = windows.ntdll.NtCancelIoFile(file.handle, &io_status_block);
8267 return e;
8268 },
8269 };
8270 defer alertable_syscall.finish();
8271 while (!done) {
8272 _ = windows.ntdll.NtDelayExecution(1, &infinite);
8273 alertable_syscall.checkCancel() catch |err| switch (err) {
8274 error.Canceled => |e| {
8275 _ = windows.ntdll.NtCancelIoFile(file.handle, &io_status_block);
8276 return e;
83458277 },
8346 else => |status| std.debug.panic("fileReadStreamingWindows NtDelayExecution returned {t}", .{status}),
8347 //else => |status| return syscall.unexpectedNtstatus(status),
8348 }
8278 };
83498279 }
83508280 }
83518281
83528282 switch (io_status_block.u.Status) {
83538283 .SUCCESS, .END_OF_FILE, .PIPE_BROKEN => {},
83548284 .ACCESS_DENIED => return error.AccessDenied,
8355 else => |status| std.debug.panic("fileReadStreamingWindows IO_STATUS_BLOCK returned {t}", .{status}),
8356 //else => |status| return windows.unexpectedStatus(status),
8285 else => |status| return windows.unexpectedStatus(status),
83578286 }
83588287 return io_status_block.Information;
83598288}
......@@ -12331,7 +12260,7 @@ fn netLookupFallible(
1233112260 var res: *ws2_32.ADDRINFOEXW = undefined;
1233212261 const timeout: ?*ws2_32.timeval = null;
1233312262 while (true) {
12334 // TODO: hook this up to cancelation with `Thread.Status.cancelation.blocked_windows_dns`.
12263 // TODO: hook this up to cancelation with `NtDelayExecution` and APC callbacks.
1233512264 try Thread.checkCancel();
1233612265 // TODO make this append to the queue eagerly rather than blocking until the whole thing finishes
1233712266 const rc: ws2_32.WinsockError = @enumFromInt(ws2_32.GetAddrInfoExW(name_w, port_w, .DNS, null, &hints, &res, timeout, null, null, null));
......@@ -16014,8 +15943,8 @@ const parking_futex = struct {
1601415943 .canceled => break :cancelable, // status is still `.canceled`
1601515944 .parked => unreachable,
1601615945 .blocked => unreachable,
16017 .blocked_apc => unreachable,
16018 .blocked_windows_dns => unreachable,
15946 .blocked_alertable => unreachable,
15947 .blocked_alertable_canceling => unreachable,
1601915948 .blocked_canceling => unreachable,
1602015949 }
1602115950 // We could now be unparked for a cancelation at any time!
......@@ -16066,8 +15995,8 @@ const parking_futex = struct {
1606615995 },
1606715996 .canceled => unreachable,
1606815997 .blocked => unreachable,
16069 .blocked_apc => unreachable,
16070 .blocked_windows_dns => unreachable,
15998 .blocked_alertable => unreachable,
15999 .blocked_alertable_canceling => unreachable,
1607116000 .blocked_canceling => unreachable,
1607216001 },
1607316002 }
......@@ -16108,8 +16037,8 @@ const parking_futex = struct {
1610816037 .canceling => continue, // race with a canceler who hasn't called `removeCanceledWaiter` yet
1610916038 .canceled => unreachable,
1611016039 .blocked => unreachable,
16111 .blocked_apc => unreachable,
16112 .blocked_windows_dns => unreachable,
16040 .blocked_alertable => unreachable,
16041 .blocked_alertable_canceling => unreachable,
1611316042 .blocked_canceling => unreachable,
1611416043 }
1611516044 // We're waking this waiter. Remove them from the bucket and add them to our local list.
......@@ -16175,8 +16104,8 @@ const parking_sleep = struct {
1617516104 .canceled => break :cancelable, // status is still `.canceled`
1617616105 .parked => unreachable,
1617716106 .blocked => unreachable,
16178 .blocked_apc => unreachable,
16179 .blocked_windows_dns => unreachable,
16107 .blocked_alertable => unreachable,
16108 .blocked_alertable_canceling => unreachable,
1618016109 .blocked_canceling => unreachable,
1618116110 }
1618216111 while (park(deadline, null)) {
......@@ -16194,8 +16123,8 @@ const parking_sleep = struct {
1619416123 .none => unreachable,
1619516124 .canceled => unreachable,
1619616125 .blocked => unreachable,
16197 .blocked_apc => unreachable,
16198 .blocked_windows_dns => unreachable,
16126 .blocked_alertable => unreachable,
16127 .blocked_alertable_canceling => unreachable,
1619916128 .blocked_canceling => unreachable,
1620016129 }
1620116130 } else |err| switch (err) {
......@@ -16214,8 +16143,8 @@ const parking_sleep = struct {
1621416143 .none => unreachable,
1621516144 .canceled => unreachable,
1621616145 .blocked => unreachable,
16217 .blocked_apc => unreachable,
16218 .blocked_windows_dns => unreachable,
16146 .blocked_alertable => unreachable,
16147 .blocked_alertable_canceling => unreachable,
1621916148 .blocked_canceling => unreachable,
1622016149 },
1622116150 }
lib/std/os/windows/ntdll.zig+1-1
......@@ -596,7 +596,7 @@ pub extern "ntdll" fn NtDelayExecution(
596596
597597pub extern "ntdll" fn NtCancelIoFileEx(
598598 FileHandle: HANDLE,
599 IoRequestToCancel: ?*IO_STATUS_BLOCK,
599 IoRequestToCancel: *const IO_STATUS_BLOCK,
600600 IoStatusBlock: *IO_STATUS_BLOCK,
601601) callconv(.winapi) NTSTATUS;
602602