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 {...@@ -339,8 +339,8 @@ const Group = struct {
339 .canceled => true,339 .canceled => true,
340 .parked => unreachable,340 .parked => unreachable,
341 .blocked => unreachable,341 .blocked => unreachable,
342 .blocked_apc => unreachable,342 .blocked_alertable => unreachable,
343 .blocked_windows_dns => unreachable,343 .blocked_alertable_canceling => unreachable,
344 .blocked_canceling => unreachable,344 .blocked_canceling => unreachable,
345 };345 };
346 if (result) {346 if (result) {
...@@ -379,10 +379,7 @@ const Group = struct {...@@ -379,10 +379,7 @@ const Group = struct {
379 while (it) |thread| : (it = thread.next) {379 while (it) |thread| : (it = thread.next) {
380 // This non-mutating RMW exists for ordering reasons: see comment in `Group.Task.start` for reasons.380 // This non-mutating RMW exists for ordering reasons: see comment in `Group.Task.start` for reasons.
381 _ = thread.status.fetchOr(.{ .cancelation = @enumFromInt(0), .awaitable = .null }, .release);381 _ = thread.status.fetchOr(.{ .cancelation = @enumFromInt(0), .awaitable = .null }, .release);
382 if (thread.cancelAwaitable(.fromGroup(g.ptr))) |method| {382 if (thread.cancelAwaitable(.fromGroup(g.ptr))) any_blocked = true;
383 thread.interrupt_method = method;
384 any_blocked = true;
385 }
386 }383 }
387 return any_blocked;384 return any_blocked;
388 }385 }
...@@ -394,7 +391,7 @@ const Group = struct {...@@ -394,7 +391,7 @@ const Group = struct {
394 var any_signaled = false;391 var any_signaled = false;
395 var it = t.worker_threads.load(.acquire); // acquire `Thread` values392 var it = t.worker_threads.load(.acquire); // acquire `Thread` values
396 while (it) |thread| : (it = thread.next) {393 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;
398 }395 }
399 return any_signaled;396 return any_signaled;
400 }397 }
...@@ -546,8 +543,8 @@ const Future = struct {...@@ -546,8 +543,8 @@ const Future = struct {
546 .canceled => true,543 .canceled => true,
547 .parked => unreachable,544 .parked => unreachable,
548 .blocked => unreachable,545 .blocked => unreachable,
549 .blocked_apc => unreachable,546 .blocked_alertable => unreachable,
550 .blocked_windows_dns => unreachable,547 .blocked_alertable_canceling => unreachable,
551 .blocked_canceling => unreachable,548 .blocked_canceling => unreachable,
552 };549 };
553 thread.status.store(.{ .cancelation = .none, .awaitable = .null }, .monotonic);550 thread.status.store(.{ .cancelation = .none, .awaitable = .null }, .monotonic);
...@@ -576,15 +573,11 @@ const Future = struct {...@@ -576,15 +573,11 @@ const Future = struct {
576 num_completed: *std.atomic.Value(u32),573 num_completed: *std.atomic.Value(u32),
577 thread: ?*Thread,574 thread: ?*Thread,
578 ) void {575 ) void {
579 var interrupt_method: ?Thread.InterruptMethod =576 var need_signal: bool = if (thread) |th| th.cancelAwaitable(.fromFuture(future)) else false;
580 if (thread) |th| th.cancelAwaitable(.fromFuture(future)) else null;
581 var timeout_ns: u64 = 1 << 10;577 var timeout_ns: u64 = 1 << 10;
582 while (true) {578 while (true) {
583 if (interrupt_method) |method| {579 need_signal = need_signal and thread.?.signalCanceledSyscall(t, .fromFuture(future));
584 if (!thread.?.signalCanceledSyscall(t, .fromFuture(future), method))580 Thread.futexWaitUncancelable(&num_completed.raw, 0, if (need_signal) timeout_ns else null);
585 interrupt_method = null;
586 }
587 Thread.futexWaitUncancelable(&num_completed.raw, 0, if (interrupt_method != null) timeout_ns else null);
588 switch (num_completed.load(.acquire)) { // acquire task results581 switch (num_completed.load(.acquire)) { // acquire task results
589 0 => {},582 0 => {},
590 1 => break,583 1 => break,
...@@ -632,17 +625,9 @@ const Thread = struct {...@@ -632,17 +625,9 @@ const Thread = struct {
632 cancel_protection: Io.CancelProtection,625 cancel_protection: Io.CancelProtection,
633 /// Always released when `Status.cancelation` is set to `.parked`.626 /// Always released when `Status.cancelation` is set to `.parked`.
634 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,627 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
639 csprng: Csprng,629 csprng: Csprng,
640630
641 const Apc = if (is_windows) struct {
642 handle: windows.HANDLE,
643 iosb: ?*windows.IO_STATUS_BLOCK,
644 } else void;
645
646 const Handle = Handle: {631 const Handle = Handle: {
647 if (std.Thread.use_pthreads) break :Handle std.c.pthread_t;632 if (std.Thread.use_pthreads) break :Handle std.c.pthread_t;
648 if (is_windows) break :Handle windows.HANDLE;633 if (is_windows) break :Handle windows.HANDLE;
...@@ -667,13 +652,11 @@ const Thread = struct {...@@ -667,13 +652,11 @@ const Thread = struct {
667 /// To request cancelation, set the status to `.blocked_canceling` and repeatedly interrupt the system call until the status changes.652 /// To request cancelation, set the status to `.blocked_canceling` and repeatedly interrupt the system call until the status changes.
668 blocked = 0b011,653 blocked = 0b011,
669654
670 /// Windows-only: the thread is blocked in a call to `NtDelayExecution`.655 /// Windows-only: the thread is blocked in an alertable wait via
671 /// To request cancelation, set the status to `.canceling` and call `NtCancelIoFileEx`.656 /// `NtDelayExecution`. To request cancelation, set the status to
672 blocked_apc = 0b100,657 /// `blocked_alertable_canceling` and repeatedly alert the thread
673658 /// until the status changes.
674 /// Windows-only: the thread is blocked in a call to `GetAddrInfoExW`.659 blocked_alertable = 0b010,
675 /// To request cancelation, set the status to `.canceling` and call `GetAddrInfoExCancel`.
676 blocked_windows_dns = 0b010,
677660
678 /// The thread has an outstanding cancelation request but is not in a cancelable operation.661 /// The thread has an outstanding cancelation request but is not in a cancelable operation.
679 /// When it acknowledges the cancelation, it will set the status to `.canceled`.662 /// When it acknowledges the cancelation, it will set the status to `.canceled`.
...@@ -722,8 +705,8 @@ const Thread = struct {...@@ -722,8 +705,8 @@ const Thread = struct {
722 switch (status.cancelation) {705 switch (status.cancelation) {
723 .parked => unreachable,706 .parked => unreachable,
724 .blocked => unreachable,707 .blocked => unreachable,
725 .blocked_apc => unreachable,708 .blocked_alertable => unreachable,
726 .blocked_windows_dns => unreachable,709 .blocked_alertable_canceling => unreachable,
727 .blocked_canceling => unreachable,710 .blocked_canceling => unreachable,
728 .none, .canceled => {},711 .none, .canceled => {},
729 .canceling => {712 .canceling => {
...@@ -1003,17 +986,17 @@ const Thread = struct {...@@ -1003,17 +986,17 @@ const Thread = struct {
1003 /// It is possible that `thread` gets canceled by this function, but is blocked in a syscall. In986 /// It is possible that `thread` gets canceled by this function, but is blocked in a syscall. In
1004 /// that case, the thread may need to be sent a signal to interrupt the call. This function will987 /// that case, the thread may need to be sent a signal to interrupt the call. This function will
1005 /// return `true` to indicate this, in which case the caller must call `signalCanceledSyscall`.988 /// 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 {
1007 var status = thread.status.load(.monotonic);990 var status = thread.status.load(.monotonic);
1008 while (true) {991 while (true) {
1009 if (status.awaitable != awaitable) return null; // thread is working on something else992 if (status.awaitable != awaitable) return false; // thread is working on something else
1010 status = switch (status.cancelation) {993 status = switch (status.cancelation) {
1011 .none => thread.status.cmpxchgWeak(994 .none => thread.status.cmpxchgWeak(
1012 .{ .cancelation = .none, .awaitable = awaitable },995 .{ .cancelation = .none, .awaitable = awaitable },
1013 .{ .cancelation = .canceling, .awaitable = awaitable },996 .{ .cancelation = .canceling, .awaitable = awaitable },
1014 .monotonic,997 .monotonic,
1015 .monotonic,998 .monotonic,
1016 ) orelse return null,999 ) orelse return false,
10171000
1018 .parked => thread.status.cmpxchgWeak(1001 .parked => thread.status.cmpxchgWeak(
1019 .{ .cancelation = .parked, .awaitable = awaitable },1002 .{ .cancelation = .parked, .awaitable = awaitable },
...@@ -1026,7 +1009,7 @@ const Thread = struct {...@@ -1026,7 +1009,7 @@ const Thread = struct {
1026 parking_futex.removeCanceledWaiter(futex_waiter);1009 parking_futex.removeCanceledWaiter(futex_waiter);
1027 }1010 }
1028 unpark(&.{thread.id}, null);1011 unpark(&.{thread.id}, null);
1029 return null;1012 return false;
1030 },1013 },
10311014
1032 .blocked => thread.status.cmpxchgWeak(1015 .blocked => thread.status.cmpxchgWeak(
...@@ -1034,17 +1017,7 @@ const Thread = struct {...@@ -1034,17 +1017,7 @@ const Thread = struct {
1034 .{ .cancelation = .blocked_canceling, .awaitable = awaitable },1017 .{ .cancelation = .blocked_canceling, .awaitable = awaitable },
1035 .monotonic,1018 .monotonic,
1036 .monotonic,1019 .monotonic,
1037 ) orelse return .sync,1020 ) orelse return true,
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 },
10481021
1049 .blocked_alertable => thread.status.cmpxchgWeak(1022 .blocked_alertable => thread.status.cmpxchgWeak(
1050 .{ .cancelation = .blocked_alertable, .awaitable = awaitable },1023 .{ .cancelation = .blocked_alertable, .awaitable = awaitable },
...@@ -1053,14 +1026,14 @@ const Thread = struct {...@@ -1053,14 +1026,14 @@ const Thread = struct {
1053 .monotonic,1026 .monotonic,
1054 ) orelse {1027 ) orelse {
1055 if (!is_windows) unreachable;1028 if (!is_windows) unreachable;
1056 return .dns;1029 return true;
1057 },1030 },
10581031
1059 .canceling, .canceled => {1032 .canceling, .canceled => {
1060 // This can happen when the task start raced with the cancelation, so the thread1033 // This can happen when the task start raced with the cancelation, so the thread
1061 // saw the cancelation on the future/group *and* we are trying to signal the1034 // saw the cancelation on the future/group *and* we are trying to signal the
1062 // thread here.1035 // thread here.
1063 return null;1036 return false;
1064 },1037 },
10651038
1066 .blocked_canceling => unreachable, // `awaitable` has not been canceled before now1039 .blocked_canceling => unreachable, // `awaitable` has not been canceled before now
...@@ -1069,11 +1042,6 @@ const Thread = struct {...@@ -1069,11 +1042,6 @@ const Thread = struct {
1069 }1042 }
1070 }1043 }
10711044
1072 const InterruptMethod = switch (native_os) {
1073 .windows => enum { sync, dns, apc },
1074 else => enum { sync },
1075 };
1076
1077 /// Sends a signal to `thread` if it is still blocked in a syscall (i.e. has not yet observed1045 /// Sends a signal to `thread` if it is still blocked in a syscall (i.e. has not yet observed
1078 /// the cancelation request from `cancelAwaitable`).1046 /// the cancelation request from `cancelAwaitable`).
1079 ///1047 ///
...@@ -1083,21 +1051,24 @@ const Thread = struct {...@@ -1083,21 +1051,24 @@ const Thread = struct {
1083 /// the thread is still blocked. For the implementation, `Future.waitForCancelWithSignaling` and1051 /// the thread is still blocked. For the implementation, `Future.waitForCancelWithSignaling` and
1084 /// `Group.waitForCancelWithSignaling`: they use exponential backoff starting at a 1us delay and1052 /// `Group.waitForCancelWithSignaling`: they use exponential backoff starting at a 1us delay and
1085 /// doubling each call. In practice, it is rare to send more than one signal.1053 /// 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 {1054 fn signalCanceledSyscall(thread: *Thread, t: *Threaded, awaitable: AwaitableId) bool {
1087 const bad_status: Status = .{ .cancelation = .blocked_canceling, .awaitable = awaitable };1055 const status = thread.status.load(.monotonic);
1088 if (thread.status.load(.monotonic) != bad_status) return false;1056 if (status.awaitable != awaitable) {
1057 // The thread has moved on and is working on something totally different.
1058 return false;
1059 }
10891060
1090 // The thread ID and/or handle can be read non-atomically because they never change and were1061 // The thread ID and/or handle can be read non-atomically because they never change and were
1091 // released by the store that made `thread` available to us.1062 // released by the store that made `thread` available to us.
10921063
1093 if (std.Thread.use_pthreads) switch (method) {1064 switch (status.cancelation) {
1094 .sync => return switch (std.c.pthread_kill(thread.handle, .IO)) {1065 .blocked_canceling => if (std.Thread.use_pthreads) {
1095 0 => true,1066 return switch (std.c.pthread_kill(thread.handle, .IO)) {
1096 else => false,1067 0 => true,
1097 },1068 else => false,
1098 } else switch (native_os) {1069 };
1099 .linux => switch (method) {1070 } else switch (native_os) {
1100 .sync => {1071 .linux => {
1101 const pid: posix.pid_t = pid: {1072 const pid: posix.pid_t = pid: {
1102 const cached_pid = @atomicLoad(Pid, &t.pid, .monotonic);1073 const cached_pid = @atomicLoad(Pid, &t.pid, .monotonic);
1103 if (cached_pid != .unknown) break :pid @intFromEnum(cached_pid);1074 if (cached_pid != .unknown) break :pid @intFromEnum(cached_pid);
...@@ -1110,9 +1081,7 @@ const Thread = struct {...@@ -1110,9 +1081,7 @@ const Thread = struct {
1110 else => false,1081 else => false,
1111 };1082 };
1112 },1083 },
1113 },1084 .windows => {
1114 .windows => switch (method) {
1115 .sync => {
1116 var iosb: windows.IO_STATUS_BLOCK = undefined;1085 var iosb: windows.IO_STATUS_BLOCK = undefined;
1117 return switch (windows.ntdll.NtCancelSynchronousIoFile(thread.handle, null, &iosb)) {1086 return switch (windows.ntdll.NtCancelSynchronousIoFile(thread.handle, null, &iosb)) {
1118 .NOT_FOUND => true, // this might mean the operation hasn't started yet1087 .NOT_FOUND => true, // this might mean the operation hasn't started yet
...@@ -1120,15 +1089,15 @@ const Thread = struct {...@@ -1120,15 +1089,15 @@ const Thread = struct {
1120 else => false,1089 else => false,
1121 };1090 };
1122 },1091 },
1123 .dns => @panic("TODO call GetAddrInfoExCancel"),1092 else => return false,
1124 .apc => {1093 },
1125 var iosb: windows.IO_STATUS_BLOCK = undefined;1094
1126 return switch (windows.ntdll.NtCancelIoFileEx(thread.apc.handle, thread.apc.iosb, &iosb)) {1095 .blocked_alertable_canceling => {
1127 .NOT_FOUND => true, // this might mean the operation hasn't started yet1096 if (!is_windows) unreachable;
1128 .SUCCESS => false, // the OS confirmed that our cancelation worked1097 return switch (windows.ntdll.NtAlertThread(thread.handle)) {
1129 else => false,1098 .SUCCESS => true,
1130 };1099 else => false,
1131 },1100 };
1132 },1101 },
11331102
1134 else => {1103 else => {
...@@ -1176,8 +1145,8 @@ const Syscall = struct {...@@ -1176,8 +1145,8 @@ const Syscall = struct {
1176 }, .monotonic).cancelation) {1145 }, .monotonic).cancelation) {
1177 .parked => unreachable,1146 .parked => unreachable,
1178 .blocked => unreachable,1147 .blocked => unreachable,
1179 .blocked_apc => unreachable,1148 .blocked_alertable => unreachable,
1180 .blocked_windows_dns => unreachable,1149 .blocked_alertable_canceling => unreachable,
1181 .blocked_canceling => unreachable,1150 .blocked_canceling => unreachable,
1182 .none => return .{ .thread = thread }, // new status is `.blocked`1151 .none => return .{ .thread = thread }, // new status is `.blocked`
1183 .canceling => return error.Canceled, // new status is `.canceled`1152 .canceling => return error.Canceled, // new status is `.canceled`
...@@ -1196,8 +1165,8 @@ const Syscall = struct {...@@ -1196,8 +1165,8 @@ const Syscall = struct {
1196 }, .monotonic).cancelation) {1165 }, .monotonic).cancelation) {
1197 .none => unreachable,1166 .none => unreachable,
1198 .parked => unreachable,1167 .parked => unreachable,
1199 .blocked_apc => unreachable,1168 .blocked_alertable => unreachable,
1200 .blocked_windows_dns => unreachable,1169 .blocked_alertable_canceling => unreachable,
1201 .canceling => unreachable,1170 .canceling => unreachable,
1202 .canceled => unreachable,1171 .canceled => unreachable,
1203 .blocked => {}, // new status is `.blocked` (unchanged)1172 .blocked => {}, // new status is `.blocked` (unchanged)
...@@ -1213,8 +1182,8 @@ const Syscall = struct {...@@ -1213,8 +1182,8 @@ const Syscall = struct {
1213 }, .monotonic).cancelation) {1182 }, .monotonic).cancelation) {
1214 .none => unreachable,1183 .none => unreachable,
1215 .parked => unreachable,1184 .parked => unreachable,
1216 .blocked_apc => unreachable,1185 .blocked_alertable => unreachable,
1217 .blocked_windows_dns => unreachable,1186 .blocked_alertable_canceling => unreachable,
1218 .canceling => unreachable,1187 .canceling => unreachable,
1219 .canceled => unreachable,1188 .canceled => unreachable,
1220 .blocked => {}, // new status is `.none`1189 .blocked => {}, // new status is `.none`
...@@ -1222,25 +1191,25 @@ const Syscall = struct {...@@ -1222,25 +1191,25 @@ const Syscall = struct {
1222 }1191 }
1223 }1192 }
1224 /// Indicates instead of `NtCancelSynchronousIoFile` we need to use1193 /// Indicates instead of `NtCancelSynchronousIoFile` we need to use
1225 /// `NtCancelIoFileEx` to interrupt the wait.1194 /// `NtAlertThread` to interrupt the wait.
1226 ///1195 ///
1227 /// Windows only, called from blocked state only.1196 /// Windows only, called from blocked state only.
1228 fn toApc(s: Syscall, apc: Thread.Apc) Io.Cancelable!void {1197 fn toAlertable(s: Syscall) Io.Cancelable!AlertableSyscall {
1229 const thread = s.thread orelse return;1198 comptime assert(is_windows);
1230 thread.apc = apc;1199 const thread = s.thread orelse return .{ .thread = null };
1231 var prev = thread.status.load(.monotonic);1200 var prev = thread.status.load(.monotonic);
1232 while (true) prev = switch (prev.cancelation) {1201 while (true) prev = switch (prev.cancelation) {
1233 .none => unreachable,1202 .none => unreachable,
1234 .parked => unreachable,1203 .parked => unreachable,
1235 .blocked_apc => unreachable,1204 .blocked_alertable => unreachable,
1236 .blocked_windows_dns => unreachable,1205 .blocked_alertable_canceling => unreachable,
1237 .canceling => unreachable,1206 .canceling => unreachable,
1238 .canceled => unreachable,1207 .canceled => unreachable,
12391208
1240 .blocked => thread.status.cmpxchgWeak(prev, .{1209 .blocked => thread.status.cmpxchgWeak(prev, .{
1241 .cancelation = .blocked_apc,1210 .cancelation = .blocked_alertable,
1242 .awaitable = prev.awaitable,1211 .awaitable = prev.awaitable,
1243 }, .monotonic, .monotonic) orelse return,1212 }, .monotonic, .monotonic) orelse return .{ .thread = thread },
12441213
1245 .blocked_canceling => thread.status.cmpxchgWeak(prev, .{1214 .blocked_canceling => thread.status.cmpxchgWeak(prev, .{
1246 .cancelation = .canceled,1215 .cancelation = .canceled,
...@@ -1248,45 +1217,6 @@ const Syscall = struct {...@@ -1248,45 +1217,6 @@ const Syscall = struct {
1248 }, .monotonic, .monotonic) orelse return error.Canceled,1217 }, .monotonic, .monotonic) orelse return error.Canceled,
1249 };1218 };
1250 }1219 }
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 }
1290 /// Convenience wrapper which calls `finish`, then returns `err`.1220 /// Convenience wrapper which calls `finish`, then returns `err`.
1291 fn fail(s: Syscall, err: anytype) @TypeOf(err) {1221 fn fail(s: Syscall, err: anytype) @TypeOf(err) {
1292 s.finish();1222 s.finish();
...@@ -1566,8 +1496,6 @@ fn worker(t: *Threaded) void {...@@ -1566,8 +1496,6 @@ fn worker(t: *Threaded) void {
1566 .cancel_protection = .unblocked,1496 .cancel_protection = .unblocked,
1567 .futex_waiter = undefined,1497 .futex_waiter = undefined,
1568 .csprng = .{},1498 .csprng = .{},
1569 .apc = undefined,
1570 .interrupt_method = undefined,
1571 };1499 };
1572 Thread.current = &thread;1500 Thread.current = &thread;
15731501
...@@ -2172,8 +2100,8 @@ fn groupAsyncEager(...@@ -2172,8 +2100,8 @@ fn groupAsyncEager(
2172 .canceled => true,2100 .canceled => true,
2173 .parked => unreachable,2101 .parked => unreachable,
2174 .blocked => unreachable,2102 .blocked => unreachable,
2175 .blocked_apc => unreachable,2103 .blocked_alertable => unreachable,
2176 .blocked_windows_dns => unreachable,2104 .blocked_alertable_canceling => unreachable,
2177 .blocked_canceling => unreachable,2105 .blocked_canceling => unreachable,
2178 };2106 };
2179 } else false;2107 } else false;
...@@ -2184,8 +2112,8 @@ fn groupAsyncEager(...@@ -2184,8 +2112,8 @@ fn groupAsyncEager(
2184 .canceled => true,2112 .canceled => true,
2185 .parked => unreachable,2113 .parked => unreachable,
2186 .blocked => unreachable,2114 .blocked => unreachable,
2187 .blocked_apc => unreachable,2115 .blocked_alertable => unreachable,
2188 .blocked_windows_dns => unreachable,2116 .blocked_alertable_canceling => unreachable,
2189 .blocked_canceling => unreachable,2117 .blocked_canceling => unreachable,
2190 };2118 };
2191 } else false;2119 } else false;
...@@ -2364,8 +2292,8 @@ fn recancelInner() void {...@@ -2364,8 +2292,8 @@ fn recancelInner() void {
2364 .canceling => unreachable, // called `recancel` but cancelation was already pending2292 .canceling => unreachable, // called `recancel` but cancelation was already pending
2365 .parked => unreachable,2293 .parked => unreachable,
2366 .blocked => unreachable,2294 .blocked => unreachable,
2367 .blocked_apc => unreachable,2295 .blocked_alertable => unreachable,
2368 .blocked_windows_dns => unreachable,2296 .blocked_alertable_canceling => unreachable,
2369 .blocked_canceling => unreachable,2297 .blocked_canceling => unreachable,
2370 }2298 }
2371}2299}
...@@ -8324,36 +8252,37 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us...@@ -8324,36 +8252,37 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us
8324 continue;8252 continue;
8325 },8253 },
8326 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err), // streaming read of async mode file8254 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err), // streaming read of async mode file
8327 else => |status| std.debug.panic("fileReadStreamingWindows NtReadFile returned {t}", .{status}),8255 else => |status| return syscall.unexpectedNtstatus(status),
8328 //else => |status| return syscall.unexpectedNtstatus(status),
8329 }8256 }
8330 }8257 }
8331 try syscall.toApc(.{ .handle = file.handle, .iosb = &io_status_block });8258 // Once we get here we received PENDING so we must not return from the
8332 while (true) {8259 // function until the operation completes.
8333 switch (windows.ntdll.NtDelayExecution(1, &infinite)) {8260 defer while (!done) {
8334 .USER_APC => {8261 _ = windows.ntdll.NtDelayExecution(1, &infinite);
8335 if (!done) {8262 };
8336 // Other APC work was queued before calling into this function.8263
8337 try syscall.checkCancelApc();8264 const alertable_syscall = syscall.toAlertable() catch |err| switch (err) {
8338 continue;8265 error.Canceled => |e| {
8339 }8266 _ = windows.ntdll.NtCancelIoFile(file.handle, &io_status_block);
8340 break syscall.finishApc();8267 return e;
8341 },8268 },
8342 .SUCCESS, .CANCELLED, .TIMEOUT, .ALERTED => {8269 };
8343 try syscall.checkCancelApc();8270 defer alertable_syscall.finish();
8344 continue;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;
8345 },8277 },
8346 else => |status| std.debug.panic("fileReadStreamingWindows NtDelayExecution returned {t}", .{status}),8278 };
8347 //else => |status| return syscall.unexpectedNtstatus(status),
8348 }
8349 }8279 }
8350 }8280 }
83518281
8352 switch (io_status_block.u.Status) {8282 switch (io_status_block.u.Status) {
8353 .SUCCESS, .END_OF_FILE, .PIPE_BROKEN => {},8283 .SUCCESS, .END_OF_FILE, .PIPE_BROKEN => {},
8354 .ACCESS_DENIED => return error.AccessDenied,8284 .ACCESS_DENIED => return error.AccessDenied,
8355 else => |status| std.debug.panic("fileReadStreamingWindows IO_STATUS_BLOCK returned {t}", .{status}),8285 else => |status| return windows.unexpectedStatus(status),
8356 //else => |status| return windows.unexpectedStatus(status),
8357 }8286 }
8358 return io_status_block.Information;8287 return io_status_block.Information;
8359}8288}
...@@ -12331,7 +12260,7 @@ fn netLookupFallible(...@@ -12331,7 +12260,7 @@ fn netLookupFallible(
12331 var res: *ws2_32.ADDRINFOEXW = undefined;12260 var res: *ws2_32.ADDRINFOEXW = undefined;
12332 const timeout: ?*ws2_32.timeval = null;12261 const timeout: ?*ws2_32.timeval = null;
12333 while (true) {12262 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.
12335 try Thread.checkCancel();12264 try Thread.checkCancel();
12336 // TODO make this append to the queue eagerly rather than blocking until the whole thing finishes12265 // TODO make this append to the queue eagerly rather than blocking until the whole thing finishes
12337 const rc: ws2_32.WinsockError = @enumFromInt(ws2_32.GetAddrInfoExW(name_w, port_w, .DNS, null, &hints, &res, timeout, null, null, null));12266 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 {...@@ -16014,8 +15943,8 @@ const parking_futex = struct {
16014 .canceled => break :cancelable, // status is still `.canceled`15943 .canceled => break :cancelable, // status is still `.canceled`
16015 .parked => unreachable,15944 .parked => unreachable,
16016 .blocked => unreachable,15945 .blocked => unreachable,
16017 .blocked_apc => unreachable,15946 .blocked_alertable => unreachable,
16018 .blocked_windows_dns => unreachable,15947 .blocked_alertable_canceling => unreachable,
16019 .blocked_canceling => unreachable,15948 .blocked_canceling => unreachable,
16020 }15949 }
16021 // We could now be unparked for a cancelation at any time!15950 // We could now be unparked for a cancelation at any time!
...@@ -16066,8 +15995,8 @@ const parking_futex = struct {...@@ -16066,8 +15995,8 @@ const parking_futex = struct {
16066 },15995 },
16067 .canceled => unreachable,15996 .canceled => unreachable,
16068 .blocked => unreachable,15997 .blocked => unreachable,
16069 .blocked_apc => unreachable,15998 .blocked_alertable => unreachable,
16070 .blocked_windows_dns => unreachable,15999 .blocked_alertable_canceling => unreachable,
16071 .blocked_canceling => unreachable,16000 .blocked_canceling => unreachable,
16072 },16001 },
16073 }16002 }
...@@ -16108,8 +16037,8 @@ const parking_futex = struct {...@@ -16108,8 +16037,8 @@ const parking_futex = struct {
16108 .canceling => continue, // race with a canceler who hasn't called `removeCanceledWaiter` yet16037 .canceling => continue, // race with a canceler who hasn't called `removeCanceledWaiter` yet
16109 .canceled => unreachable,16038 .canceled => unreachable,
16110 .blocked => unreachable,16039 .blocked => unreachable,
16111 .blocked_apc => unreachable,16040 .blocked_alertable => unreachable,
16112 .blocked_windows_dns => unreachable,16041 .blocked_alertable_canceling => unreachable,
16113 .blocked_canceling => unreachable,16042 .blocked_canceling => unreachable,
16114 }16043 }
16115 // We're waking this waiter. Remove them from the bucket and add them to our local list.16044 // 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 {...@@ -16175,8 +16104,8 @@ const parking_sleep = struct {
16175 .canceled => break :cancelable, // status is still `.canceled`16104 .canceled => break :cancelable, // status is still `.canceled`
16176 .parked => unreachable,16105 .parked => unreachable,
16177 .blocked => unreachable,16106 .blocked => unreachable,
16178 .blocked_apc => unreachable,16107 .blocked_alertable => unreachable,
16179 .blocked_windows_dns => unreachable,16108 .blocked_alertable_canceling => unreachable,
16180 .blocked_canceling => unreachable,16109 .blocked_canceling => unreachable,
16181 }16110 }
16182 while (park(deadline, null)) {16111 while (park(deadline, null)) {
...@@ -16194,8 +16123,8 @@ const parking_sleep = struct {...@@ -16194,8 +16123,8 @@ const parking_sleep = struct {
16194 .none => unreachable,16123 .none => unreachable,
16195 .canceled => unreachable,16124 .canceled => unreachable,
16196 .blocked => unreachable,16125 .blocked => unreachable,
16197 .blocked_apc => unreachable,16126 .blocked_alertable => unreachable,
16198 .blocked_windows_dns => unreachable,16127 .blocked_alertable_canceling => unreachable,
16199 .blocked_canceling => unreachable,16128 .blocked_canceling => unreachable,
16200 }16129 }
16201 } else |err| switch (err) {16130 } else |err| switch (err) {
...@@ -16214,8 +16143,8 @@ const parking_sleep = struct {...@@ -16214,8 +16143,8 @@ const parking_sleep = struct {
16214 .none => unreachable,16143 .none => unreachable,
16215 .canceled => unreachable,16144 .canceled => unreachable,
16216 .blocked => unreachable,16145 .blocked => unreachable,
16217 .blocked_apc => unreachable,16146 .blocked_alertable => unreachable,
16218 .blocked_windows_dns => unreachable,16147 .blocked_alertable_canceling => unreachable,
16219 .blocked_canceling => unreachable,16148 .blocked_canceling => unreachable,
16220 },16149 },
16221 }16150 }
lib/std/os/windows/ntdll.zig+1-1
...@@ -596,7 +596,7 @@ pub extern "ntdll" fn NtDelayExecution(...@@ -596,7 +596,7 @@ pub extern "ntdll" fn NtDelayExecution(
596596
597pub extern "ntdll" fn NtCancelIoFileEx(597pub extern "ntdll" fn NtCancelIoFileEx(
598 FileHandle: HANDLE,598 FileHandle: HANDLE,
599 IoRequestToCancel: ?*IO_STATUS_BLOCK,599 IoRequestToCancel: *const IO_STATUS_BLOCK,
600 IoStatusBlock: *IO_STATUS_BLOCK,600 IoStatusBlock: *IO_STATUS_BLOCK,
601) callconv(.winapi) NTSTATUS;601) callconv(.winapi) NTSTATUS;
602602