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 {...@@ -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
...@@ -2176,8 +2104,8 @@ fn groupAsyncEager(...@@ -2176,8 +2104,8 @@ fn groupAsyncEager(
2176 .canceled => true,2104 .canceled => true,
2177 .parked => unreachable,2105 .parked => unreachable,
2178 .blocked => unreachable,2106 .blocked => unreachable,
2179 .blocked_apc => unreachable,2107 .blocked_alertable => unreachable,
2180 .blocked_windows_dns => unreachable,2108 .blocked_alertable_canceling => unreachable,
2181 .blocked_canceling => unreachable,2109 .blocked_canceling => unreachable,
2182 };2110 };
2183 } else false;2111 } else false;
...@@ -2188,8 +2116,8 @@ fn groupAsyncEager(...@@ -2188,8 +2116,8 @@ fn groupAsyncEager(
2188 .canceled => true,2116 .canceled => true,
2189 .parked => unreachable,2117 .parked => unreachable,
2190 .blocked => unreachable,2118 .blocked => unreachable,
2191 .blocked_apc => unreachable,2119 .blocked_alertable => unreachable,
2192 .blocked_windows_dns => unreachable,2120 .blocked_alertable_canceling => unreachable,
2193 .blocked_canceling => unreachable,2121 .blocked_canceling => unreachable,
2194 };2122 };
2195 } else false;2123 } else false;
...@@ -2368,8 +2296,8 @@ fn recancelInner() void {...@@ -2368,8 +2296,8 @@ fn recancelInner() void {
2368 .canceling => unreachable, // called `recancel` but cancelation was already pending2296 .canceling => unreachable, // called `recancel` but cancelation was already pending
2369 .parked => unreachable,2297 .parked => unreachable,
2370 .blocked => unreachable,2298 .blocked => unreachable,
2371 .blocked_apc => unreachable,2299 .blocked_alertable => unreachable,
2372 .blocked_windows_dns => unreachable,2300 .blocked_alertable_canceling => unreachable,
2373 .blocked_canceling => unreachable,2301 .blocked_canceling => unreachable,
2374 }2302 }
2375}2303}
...@@ -8467,36 +8395,37 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us...@@ -8467,36 +8395,37 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us
8467 continue;8395 continue;
8468 },8396 },
8469 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err), // streaming read of async mode file8397 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err), // streaming read of async mode file
8470 else => |status| std.debug.panic("fileReadStreamingWindows NtReadFile returned {t}", .{status}),8398 else => |status| return syscall.unexpectedNtstatus(status),
8471 //else => |status| return syscall.unexpectedNtstatus(status),
8472 }8399 }
8473 }8400 }
8474 try syscall.toApc(.{ .handle = file.handle, .iosb = &io_status_block });8401 // Once we get here we received PENDING so we must not return from the
8475 while (true) {8402 // function until the operation completes.
8476 switch (windows.ntdll.NtDelayExecution(1, &infinite)) {8403 defer while (!done) {
8477 .USER_APC => {8404 _ = windows.ntdll.NtDelayExecution(1, &infinite);
8478 if (!done) {8405 };
8479 // Other APC work was queued before calling into this function.8406
8480 try syscall.checkCancelApc();8407 const alertable_syscall = syscall.toAlertable() catch |err| switch (err) {
8481 continue;8408 error.Canceled => |e| {
8482 }8409 _ = windows.ntdll.NtCancelIoFile(file.handle, &io_status_block);
8483 break syscall.finishApc();8410 return e;
8484 },8411 },
8485 .SUCCESS, .CANCELLED, .TIMEOUT, .ALERTED => {8412 };
8486 try syscall.checkCancelApc();8413 defer alertable_syscall.finish();
8487 continue;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;
8488 },8420 },
8489 else => |status| std.debug.panic("fileReadStreamingWindows NtDelayExecution returned {t}", .{status}),8421 };
8490 //else => |status| return syscall.unexpectedNtstatus(status),
8491 }
8492 }8422 }
8493 }8423 }
84948424
8495 switch (io_status_block.u.Status) {8425 switch (io_status_block.u.Status) {
8496 .SUCCESS, .END_OF_FILE, .PIPE_BROKEN => {},8426 .SUCCESS, .END_OF_FILE, .PIPE_BROKEN => {},
8497 .ACCESS_DENIED => return error.AccessDenied,8427 .ACCESS_DENIED => return error.AccessDenied,
8498 else => |status| std.debug.panic("fileReadStreamingWindows IO_STATUS_BLOCK returned {t}", .{status}),8428 else => |status| return windows.unexpectedStatus(status),
8499 //else => |status| return windows.unexpectedStatus(status),
8500 }8429 }
8501 return io_status_block.Information;8430 return io_status_block.Information;
8502}8431}
...@@ -12519,7 +12448,7 @@ fn netLookupFallible(...@@ -12519,7 +12448,7 @@ fn netLookupFallible(
12519 var res: *ws2_32.ADDRINFOEXW = undefined;12448 var res: *ws2_32.ADDRINFOEXW = undefined;
12520 const timeout: ?*ws2_32.timeval = null;12449 const timeout: ?*ws2_32.timeval = null;
12521 while (true) {12450 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.
12523 try Thread.checkCancel();12452 try Thread.checkCancel();
12524 // TODO make this append to the queue eagerly rather than blocking until the whole thing finishes12453 // TODO make this append to the queue eagerly rather than blocking until the whole thing finishes
12525 const rc: ws2_32.WinsockError = @enumFromInt(ws2_32.GetAddrInfoExW(name_w, port_w, .DNS, null, &hints, &res, timeout, null, null, null));12454 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 {...@@ -16253,8 +16182,8 @@ const parking_futex = struct {
16253 .canceled => break :cancelable, // status is still `.canceled`16182 .canceled => break :cancelable, // status is still `.canceled`
16254 .parked => unreachable,16183 .parked => unreachable,
16255 .blocked => unreachable,16184 .blocked => unreachable,
16256 .blocked_apc => unreachable,16185 .blocked_alertable => unreachable,
16257 .blocked_windows_dns => unreachable,16186 .blocked_alertable_canceling => unreachable,
16258 .blocked_canceling => unreachable,16187 .blocked_canceling => unreachable,
16259 }16188 }
16260 // We could now be unparked for a cancelation at any time!16189 // We could now be unparked for a cancelation at any time!
...@@ -16305,8 +16234,8 @@ const parking_futex = struct {...@@ -16305,8 +16234,8 @@ const parking_futex = struct {
16305 },16234 },
16306 .canceled => unreachable,16235 .canceled => unreachable,
16307 .blocked => unreachable,16236 .blocked => unreachable,
16308 .blocked_apc => unreachable,16237 .blocked_alertable => unreachable,
16309 .blocked_windows_dns => unreachable,16238 .blocked_alertable_canceling => unreachable,
16310 .blocked_canceling => unreachable,16239 .blocked_canceling => unreachable,
16311 },16240 },
16312 }16241 }
...@@ -16347,8 +16276,8 @@ const parking_futex = struct {...@@ -16347,8 +16276,8 @@ const parking_futex = struct {
16347 .canceling => continue, // race with a canceler who hasn't called `removeCanceledWaiter` yet16276 .canceling => continue, // race with a canceler who hasn't called `removeCanceledWaiter` yet
16348 .canceled => unreachable,16277 .canceled => unreachable,
16349 .blocked => unreachable,16278 .blocked => unreachable,
16350 .blocked_apc => unreachable,16279 .blocked_alertable => unreachable,
16351 .blocked_windows_dns => unreachable,16280 .blocked_alertable_canceling => unreachable,
16352 .blocked_canceling => unreachable,16281 .blocked_canceling => unreachable,
16353 }16282 }
16354 // We're waking this waiter. Remove them from the bucket and add them to our local list.16283 // 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 {...@@ -16414,8 +16343,8 @@ const parking_sleep = struct {
16414 .canceled => break :cancelable, // status is still `.canceled`16343 .canceled => break :cancelable, // status is still `.canceled`
16415 .parked => unreachable,16344 .parked => unreachable,
16416 .blocked => unreachable,16345 .blocked => unreachable,
16417 .blocked_apc => unreachable,16346 .blocked_alertable => unreachable,
16418 .blocked_windows_dns => unreachable,16347 .blocked_alertable_canceling => unreachable,
16419 .blocked_canceling => unreachable,16348 .blocked_canceling => unreachable,
16420 }16349 }
16421 while (park(deadline, null)) {16350 while (park(deadline, null)) {
...@@ -16433,8 +16362,8 @@ const parking_sleep = struct {...@@ -16433,8 +16362,8 @@ const parking_sleep = struct {
16433 .none => unreachable,16362 .none => unreachable,
16434 .canceled => unreachable,16363 .canceled => unreachable,
16435 .blocked => unreachable,16364 .blocked => unreachable,
16436 .blocked_apc => unreachable,16365 .blocked_alertable => unreachable,
16437 .blocked_windows_dns => unreachable,16366 .blocked_alertable_canceling => unreachable,
16438 .blocked_canceling => unreachable,16367 .blocked_canceling => unreachable,
16439 }16368 }
16440 } else |err| switch (err) {16369 } else |err| switch (err) {
...@@ -16453,8 +16382,8 @@ const parking_sleep = struct {...@@ -16453,8 +16382,8 @@ const parking_sleep = struct {
16453 .none => unreachable,16382 .none => unreachable,
16454 .canceled => unreachable,16383 .canceled => unreachable,
16455 .blocked => unreachable,16384 .blocked => unreachable,
16456 .blocked_apc => unreachable,16385 .blocked_alertable => unreachable,
16457 .blocked_windows_dns => unreachable,16386 .blocked_alertable_canceling => unreachable,
16458 .blocked_canceling => unreachable,16387 .blocked_canceling => unreachable,
16459 },16388 },
16460 }16389 }
lib/std/os/windows/ntdll.zig+1-1
...@@ -601,7 +601,7 @@ pub extern "ntdll" fn NtDelayExecution(...@@ -601,7 +601,7 @@ pub extern "ntdll" fn NtDelayExecution(
601601
602pub extern "ntdll" fn NtCancelIoFileEx(602pub extern "ntdll" fn NtCancelIoFileEx(
603 FileHandle: HANDLE,603 FileHandle: HANDLE,
604 IoRequestToCancel: ?*IO_STATUS_BLOCK,604 IoRequestToCancel: *const IO_STATUS_BLOCK,
605 IoStatusBlock: *IO_STATUS_BLOCK,605 IoStatusBlock: *IO_STATUS_BLOCK,
606) callconv(.winapi) NTSTATUS;606) callconv(.winapi) NTSTATUS;
607607