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-30 12:10:01-08:00
logb47080026508f411c9fbda53d62d15dc5e90c2f9
tree95d57e6247b1b9e8f1710dd67bc4cb09f91b8be0
parent84303546ba603623070932432efb43c413d48a0e

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
......@@ -2176,8 +2104,8 @@ fn groupAsyncEager(
21762104 .canceled => true,
21772105 .parked => unreachable,
21782106 .blocked => unreachable,
2179 .blocked_apc => unreachable,
2180 .blocked_windows_dns => unreachable,
2107 .blocked_alertable => unreachable,
2108 .blocked_alertable_canceling => unreachable,
21812109 .blocked_canceling => unreachable,
21822110 };
21832111 } else false;
......@@ -2188,8 +2116,8 @@ fn groupAsyncEager(
21882116 .canceled => true,
21892117 .parked => unreachable,
21902118 .blocked => unreachable,
2191 .blocked_apc => unreachable,
2192 .blocked_windows_dns => unreachable,
2119 .blocked_alertable => unreachable,
2120 .blocked_alertable_canceling => unreachable,
21932121 .blocked_canceling => unreachable,
21942122 };
21952123 } else false;
......@@ -2368,8 +2296,8 @@ fn recancelInner() void {
23682296 .canceling => unreachable, // called `recancel` but cancelation was already pending
23692297 .parked => unreachable,
23702298 .blocked => unreachable,
2371 .blocked_apc => unreachable,
2372 .blocked_windows_dns => unreachable,
2299 .blocked_alertable => unreachable,
2300 .blocked_alertable_canceling => unreachable,
23732301 .blocked_canceling => unreachable,
23742302 }
23752303}
......@@ -8467,36 +8395,37 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us
84678395 continue;
84688396 },
84698397 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err), // streaming read of async mode file
8470 else => |status| std.debug.panic("fileReadStreamingWindows NtReadFile returned {t}", .{status}),
8471 //else => |status| return syscall.unexpectedNtstatus(status),
8398 else => |status| return syscall.unexpectedNtstatus(status),
84728399 }
84738400 }
8474 try syscall.toApc(.{ .handle = file.handle, .iosb = &io_status_block });
8475 while (true) {
8476 switch (windows.ntdll.NtDelayExecution(1, &infinite)) {
8477 .USER_APC => {
8478 if (!done) {
8479 // Other APC work was queued before calling into this function.
8480 try syscall.checkCancelApc();
8481 continue;
8482 }
8483 break syscall.finishApc();
8484 },
8485 .SUCCESS, .CANCELLED, .TIMEOUT, .ALERTED => {
8486 try syscall.checkCancelApc();
8487 continue;
8401 // Once we get here we received PENDING so we must not return from the
8402 // function until the operation completes.
8403 defer while (!done) {
8404 _ = windows.ntdll.NtDelayExecution(1, &infinite);
8405 };
8406
8407 const alertable_syscall = syscall.toAlertable() catch |err| switch (err) {
8408 error.Canceled => |e| {
8409 _ = windows.ntdll.NtCancelIoFile(file.handle, &io_status_block);
8410 return e;
8411 },
8412 };
8413 defer alertable_syscall.finish();
8414 while (!done) {
8415 _ = windows.ntdll.NtDelayExecution(1, &infinite);
8416 alertable_syscall.checkCancel() catch |err| switch (err) {
8417 error.Canceled => |e| {
8418 _ = windows.ntdll.NtCancelIoFile(file.handle, &io_status_block);
8419 return e;
84888420 },
8489 else => |status| std.debug.panic("fileReadStreamingWindows NtDelayExecution returned {t}", .{status}),
8490 //else => |status| return syscall.unexpectedNtstatus(status),
8491 }
8421 };
84928422 }
84938423 }
84948424
84958425 switch (io_status_block.u.Status) {
84968426 .SUCCESS, .END_OF_FILE, .PIPE_BROKEN => {},
84978427 .ACCESS_DENIED => return error.AccessDenied,
8498 else => |status| std.debug.panic("fileReadStreamingWindows IO_STATUS_BLOCK returned {t}", .{status}),
8499 //else => |status| return windows.unexpectedStatus(status),
8428 else => |status| return windows.unexpectedStatus(status),
85008429 }
85018430 return io_status_block.Information;
85028431}
......@@ -12519,7 +12448,7 @@ fn netLookupFallible(
1251912448 var res: *ws2_32.ADDRINFOEXW = undefined;
1252012449 const timeout: ?*ws2_32.timeval = null;
1252112450 while (true) {
12522 // TODO: hook this up to cancelation with `Thread.Status.cancelation.blocked_windows_dns`.
12451 // TODO: hook this up to cancelation with `NtDelayExecution` and APC callbacks.
1252312452 try Thread.checkCancel();
1252412453 // TODO make this append to the queue eagerly rather than blocking until the whole thing finishes
1252512454 const rc: ws2_32.WinsockError = @enumFromInt(ws2_32.GetAddrInfoExW(name_w, port_w, .DNS, null, &hints, &res, timeout, null, null, null));
......@@ -16253,8 +16182,8 @@ const parking_futex = struct {
1625316182 .canceled => break :cancelable, // status is still `.canceled`
1625416183 .parked => unreachable,
1625516184 .blocked => unreachable,
16256 .blocked_apc => unreachable,
16257 .blocked_windows_dns => unreachable,
16185 .blocked_alertable => unreachable,
16186 .blocked_alertable_canceling => unreachable,
1625816187 .blocked_canceling => unreachable,
1625916188 }
1626016189 // We could now be unparked for a cancelation at any time!
......@@ -16305,8 +16234,8 @@ const parking_futex = struct {
1630516234 },
1630616235 .canceled => unreachable,
1630716236 .blocked => unreachable,
16308 .blocked_apc => unreachable,
16309 .blocked_windows_dns => unreachable,
16237 .blocked_alertable => unreachable,
16238 .blocked_alertable_canceling => unreachable,
1631016239 .blocked_canceling => unreachable,
1631116240 },
1631216241 }
......@@ -16347,8 +16276,8 @@ const parking_futex = struct {
1634716276 .canceling => continue, // race with a canceler who hasn't called `removeCanceledWaiter` yet
1634816277 .canceled => unreachable,
1634916278 .blocked => unreachable,
16350 .blocked_apc => unreachable,
16351 .blocked_windows_dns => unreachable,
16279 .blocked_alertable => unreachable,
16280 .blocked_alertable_canceling => unreachable,
1635216281 .blocked_canceling => unreachable,
1635316282 }
1635416283 // We're waking this waiter. Remove them from the bucket and add them to our local list.
......@@ -16414,8 +16343,8 @@ const parking_sleep = struct {
1641416343 .canceled => break :cancelable, // status is still `.canceled`
1641516344 .parked => unreachable,
1641616345 .blocked => unreachable,
16417 .blocked_apc => unreachable,
16418 .blocked_windows_dns => unreachable,
16346 .blocked_alertable => unreachable,
16347 .blocked_alertable_canceling => unreachable,
1641916348 .blocked_canceling => unreachable,
1642016349 }
1642116350 while (park(deadline, null)) {
......@@ -16433,8 +16362,8 @@ const parking_sleep = struct {
1643316362 .none => unreachable,
1643416363 .canceled => unreachable,
1643516364 .blocked => unreachable,
16436 .blocked_apc => unreachable,
16437 .blocked_windows_dns => unreachable,
16365 .blocked_alertable => unreachable,
16366 .blocked_alertable_canceling => unreachable,
1643816367 .blocked_canceling => unreachable,
1643916368 }
1644016369 } else |err| switch (err) {
......@@ -16453,8 +16382,8 @@ const parking_sleep = struct {
1645316382 .none => unreachable,
1645416383 .canceled => unreachable,
1645516384 .blocked => unreachable,
16456 .blocked_apc => unreachable,
16457 .blocked_windows_dns => unreachable,
16385 .blocked_alertable => unreachable,
16386 .blocked_alertable_canceling => unreachable,
1645816387 .blocked_canceling => unreachable,
1645916388 },
1646016389 }
lib/std/os/windows/ntdll.zig+1-1
......@@ -601,7 +601,7 @@ pub extern "ntdll" fn NtDelayExecution(
601601
602602pub extern "ntdll" fn NtCancelIoFileEx(
603603 FileHandle: HANDLE,
604 IoRequestToCancel: ?*IO_STATUS_BLOCK,
604 IoRequestToCancel: *const IO_STATUS_BLOCK,
605605 IoStatusBlock: *IO_STATUS_BLOCK,
606606) callconv(.winapi) NTSTATUS;
607607