authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-21 21:05:15-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-27 15:32:34-08:00
loge18ee3a93afb7cb090ac6ef2cd49d2386edd3950
treef5ec3e8725f48ca1f879f832fb4e8aa45f328886
parent20c4211b6e4fffe47330fa2a60e42ac39e78044c

std.Io.Threaded: introduce Thread.InterruptMethod

implements APC cancelation except for the actual call to NtCancelIoFileEx

1 files changed, 149 insertions(+), 92 deletions(-)

lib/std/Io/Threaded.zig+149-92
...@@ -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_alertable => unreachable,342 .blocked_apc => unreachable,
343 .blocked_alertable_canceling => unreachable,343 .blocked_windows_dns => unreachable,
344 .blocked_canceling => unreachable,344 .blocked_canceling => unreachable,
345 };345 };
346 if (result) {346 if (result) {
...@@ -379,7 +379,10 @@ const Group = struct {...@@ -379,7 +379,10 @@ 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))) any_blocked = true;382 if (thread.cancelAwaitable(.fromGroup(g.ptr))) |method| {
383 thread.interrupt_method = method;
384 any_blocked = true;
385 }
383 }386 }
384 return any_blocked;387 return any_blocked;
385 }388 }
...@@ -391,7 +394,7 @@ const Group = struct {...@@ -391,7 +394,7 @@ const Group = struct {
391 var any_signaled = false;394 var any_signaled = false;
392 var it = t.worker_threads.load(.acquire); // acquire `Thread` values395 var it = t.worker_threads.load(.acquire); // acquire `Thread` values
393 while (it) |thread| : (it = thread.next) {396 while (it) |thread| : (it = thread.next) {
394 if (thread.signalCanceledSyscall(t, .fromGroup(g.ptr))) any_signaled = true;397 if (thread.signalCanceledSyscall(t, .fromGroup(g.ptr), thread.interrupt_method)) any_signaled = true;
395 }398 }
396 return any_signaled;399 return any_signaled;
397 }400 }
...@@ -543,8 +546,8 @@ const Future = struct {...@@ -543,8 +546,8 @@ const Future = struct {
543 .canceled => true,546 .canceled => true,
544 .parked => unreachable,547 .parked => unreachable,
545 .blocked => unreachable,548 .blocked => unreachable,
546 .blocked_alertable => unreachable,549 .blocked_apc => unreachable,
547 .blocked_alertable_canceling => unreachable,550 .blocked_windows_dns => unreachable,
548 .blocked_canceling => unreachable,551 .blocked_canceling => unreachable,
549 };552 };
550 thread.status.store(.{ .cancelation = .none, .awaitable = .null }, .monotonic);553 thread.status.store(.{ .cancelation = .none, .awaitable = .null }, .monotonic);
...@@ -573,11 +576,15 @@ const Future = struct {...@@ -573,11 +576,15 @@ const Future = struct {
573 num_completed: *std.atomic.Value(u32),576 num_completed: *std.atomic.Value(u32),
574 thread: ?*Thread,577 thread: ?*Thread,
575 ) void {578 ) void {
576 var need_signal: bool = if (thread) |th| th.cancelAwaitable(.fromFuture(future)) else false;579 var interrupt_method: ?Thread.InterruptMethod =
580 if (thread) |th| th.cancelAwaitable(.fromFuture(future)) else null;
577 var timeout_ns: u64 = 1 << 10;581 var timeout_ns: u64 = 1 << 10;
578 while (true) {582 while (true) {
579 need_signal = need_signal and thread.?.signalCanceledSyscall(t, .fromFuture(future));583 if (interrupt_method) |method| {
580 Thread.futexWaitUncancelable(&num_completed.raw, 0, if (need_signal) timeout_ns else null);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);
581 switch (num_completed.load(.acquire)) { // acquire task results588 switch (num_completed.load(.acquire)) { // acquire task results
582 0 => {},589 0 => {},
583 1 => break,590 1 => break,
...@@ -625,6 +632,9 @@ const Thread = struct {...@@ -625,6 +632,9 @@ const Thread = struct {
625 cancel_protection: Io.CancelProtection,632 cancel_protection: Io.CancelProtection,
626 /// Always released when `Status.cancelation` is set to `.parked`.633 /// Always released when `Status.cancelation` is set to `.parked`.
627 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,634 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,
635 apc_context: if (is_windows) ?*anyopaque else void,
636 /// Used only by group cancelation code for temporary storage.
637 interrupt_method: InterruptMethod,
628638
629 csprng: Csprng,639 csprng: Csprng,
630640
...@@ -652,11 +662,13 @@ const Thread = struct {...@@ -652,11 +662,13 @@ const Thread = struct {
652 /// To request cancelation, set the status to `.blocked_canceling` and repeatedly interrupt the system call until the status changes.662 /// To request cancelation, set the status to `.blocked_canceling` and repeatedly interrupt the system call until the status changes.
653 blocked = 0b011,663 blocked = 0b011,
654664
655 /// Windows-only: the thread is blocked in an alertable wait via665 /// Windows-only: the thread is blocked in a call to `NtDelayExecution`.
656 /// `NtDelayExecution`. To request cancelation, set the status to666 /// To request cancelation, set the status to `.canceling` and call `NtCancelIoFileEx`.
657 /// `blocked_alertable_canceling` and repeatedly alert the thread667 blocked_apc = 0b100,
658 /// until the status changes.668
659 blocked_alertable = 0b010,669 /// Windows-only: the thread is blocked in a call to `GetAddrInfoExW`.
670 /// To request cancelation, set the status to `.canceling` and call `GetAddrInfoExCancel`.
671 blocked_windows_dns = 0b010,
660672
661 /// The thread has an outstanding cancelation request but is not in a cancelable operation.673 /// The thread has an outstanding cancelation request but is not in a cancelable operation.
662 /// When it acknowledges the cancelation, it will set the status to `.canceled`.674 /// When it acknowledges the cancelation, it will set the status to `.canceled`.
...@@ -705,8 +717,8 @@ const Thread = struct {...@@ -705,8 +717,8 @@ const Thread = struct {
705 switch (status.cancelation) {717 switch (status.cancelation) {
706 .parked => unreachable,718 .parked => unreachable,
707 .blocked => unreachable,719 .blocked => unreachable,
708 .blocked_alertable => unreachable,720 .blocked_apc => unreachable,
709 .blocked_alertable_canceling => unreachable,721 .blocked_windows_dns => unreachable,
710 .blocked_canceling => unreachable,722 .blocked_canceling => unreachable,
711 .none, .canceled => {},723 .none, .canceled => {},
712 .canceling => {724 .canceling => {
...@@ -986,17 +998,17 @@ const Thread = struct {...@@ -986,17 +998,17 @@ const Thread = struct {
986 /// It is possible that `thread` gets canceled by this function, but is blocked in a syscall. In998 /// It is possible that `thread` gets canceled by this function, but is blocked in a syscall. In
987 /// that case, the thread may need to be sent a signal to interrupt the call. This function will999 /// that case, the thread may need to be sent a signal to interrupt the call. This function will
988 /// return `true` to indicate this, in which case the caller must call `signalCanceledSyscall`.1000 /// return `true` to indicate this, in which case the caller must call `signalCanceledSyscall`.
989 fn cancelAwaitable(thread: *Thread, awaitable: AwaitableId) bool {1001 fn cancelAwaitable(thread: *Thread, awaitable: AwaitableId) ?InterruptMethod {
990 var status = thread.status.load(.monotonic);1002 var status = thread.status.load(.monotonic);
991 while (true) {1003 while (true) {
992 if (status.awaitable != awaitable) return false; // thread is working on something else1004 if (status.awaitable != awaitable) return null; // thread is working on something else
993 status = switch (status.cancelation) {1005 status = switch (status.cancelation) {
994 .none => thread.status.cmpxchgWeak(1006 .none => thread.status.cmpxchgWeak(
995 .{ .cancelation = .none, .awaitable = awaitable },1007 .{ .cancelation = .none, .awaitable = awaitable },
996 .{ .cancelation = .canceling, .awaitable = awaitable },1008 .{ .cancelation = .canceling, .awaitable = awaitable },
997 .monotonic,1009 .monotonic,
998 .monotonic,1010 .monotonic,
999 ) orelse return false,1011 ) orelse return null,
10001012
1001 .parked => thread.status.cmpxchgWeak(1013 .parked => thread.status.cmpxchgWeak(
1002 .{ .cancelation = .parked, .awaitable = awaitable },1014 .{ .cancelation = .parked, .awaitable = awaitable },
...@@ -1009,7 +1021,7 @@ const Thread = struct {...@@ -1009,7 +1021,7 @@ const Thread = struct {
1009 parking_futex.removeCanceledWaiter(futex_waiter);1021 parking_futex.removeCanceledWaiter(futex_waiter);
1010 }1022 }
1011 unpark(&.{thread.id}, null);1023 unpark(&.{thread.id}, null);
1012 return false;1024 return null;
1013 },1025 },
10141026
1015 .blocked => thread.status.cmpxchgWeak(1027 .blocked => thread.status.cmpxchgWeak(
...@@ -1017,7 +1029,17 @@ const Thread = struct {...@@ -1017,7 +1029,17 @@ const Thread = struct {
1017 .{ .cancelation = .blocked_canceling, .awaitable = awaitable },1029 .{ .cancelation = .blocked_canceling, .awaitable = awaitable },
1018 .monotonic,1030 .monotonic,
1019 .monotonic,1031 .monotonic,
1020 ) orelse return true,1032 ) orelse return .sync,
1033
1034 .blocked_apc => thread.status.cmpxchgWeak(
1035 .{ .cancelation = .blocked_apc, .awaitable = awaitable },
1036 .{ .cancelation = .canceling, .awaitable = awaitable },
1037 .monotonic,
1038 .monotonic,
1039 ) orelse {
1040 if (!is_windows) unreachable;
1041 return .apc;
1042 },
10211043
1022 .blocked_alertable => thread.status.cmpxchgWeak(1044 .blocked_alertable => thread.status.cmpxchgWeak(
1023 .{ .cancelation = .blocked_alertable, .awaitable = awaitable },1045 .{ .cancelation = .blocked_alertable, .awaitable = awaitable },
...@@ -1026,14 +1048,14 @@ const Thread = struct {...@@ -1026,14 +1048,14 @@ const Thread = struct {
1026 .monotonic,1048 .monotonic,
1027 ) orelse {1049 ) orelse {
1028 if (!is_windows) unreachable;1050 if (!is_windows) unreachable;
1029 return true;1051 return .dns;
1030 },1052 },
10311053
1032 .canceling, .canceled => {1054 .canceling, .canceled => {
1033 // This can happen when the task start raced with the cancelation, so the thread1055 // This can happen when the task start raced with the cancelation, so the thread
1034 // saw the cancelation on the future/group *and* we are trying to signal the1056 // saw the cancelation on the future/group *and* we are trying to signal the
1035 // thread here.1057 // thread here.
1036 return false;1058 return null;
1037 },1059 },
10381060
1039 .blocked_canceling => unreachable, // `awaitable` has not been canceled before now1061 .blocked_canceling => unreachable, // `awaitable` has not been canceled before now
...@@ -1042,6 +1064,11 @@ const Thread = struct {...@@ -1042,6 +1064,11 @@ const Thread = struct {
1042 }1064 }
1043 }1065 }
10441066
1067 const InterruptMethod = switch (native_os) {
1068 .windows => enum { sync, dns, apc },
1069 else => enum { sync },
1070 };
1071
1045 /// Sends a signal to `thread` if it is still blocked in a syscall (i.e. has not yet observed1072 /// Sends a signal to `thread` if it is still blocked in a syscall (i.e. has not yet observed
1046 /// the cancelation request from `cancelAwaitable`).1073 /// the cancelation request from `cancelAwaitable`).
1047 ///1074 ///
...@@ -1051,24 +1078,21 @@ const Thread = struct {...@@ -1051,24 +1078,21 @@ const Thread = struct {
1051 /// the thread is still blocked. For the implementation, `Future.waitForCancelWithSignaling` and1078 /// the thread is still blocked. For the implementation, `Future.waitForCancelWithSignaling` and
1052 /// `Group.waitForCancelWithSignaling`: they use exponential backoff starting at a 1us delay and1079 /// `Group.waitForCancelWithSignaling`: they use exponential backoff starting at a 1us delay and
1053 /// doubling each call. In practice, it is rare to send more than one signal.1080 /// doubling each call. In practice, it is rare to send more than one signal.
1054 fn signalCanceledSyscall(thread: *Thread, t: *Threaded, awaitable: AwaitableId) bool {1081 fn signalCanceledSyscall(thread: *Thread, t: *Threaded, awaitable: AwaitableId, method: InterruptMethod) bool {
1055 const status = thread.status.load(.monotonic);1082 const bad_status: Status = .{ .cancelation = .blocked_canceling, .awaitable = awaitable };
1056 if (status.awaitable != awaitable) {1083 if (thread.status.load(.monotonic) != bad_status) return false;
1057 // The thread has moved on and is working on something totally different.
1058 return false;
1059 }
10601084
1061 // The thread ID and/or handle can be read non-atomically because they never change and were1085 // The thread ID and/or handle can be read non-atomically because they never change and were
1062 // released by the store that made `thread` available to us.1086 // released by the store that made `thread` available to us.
10631087
1064 switch (status.cancelation) {1088 if (std.Thread.use_pthreads) switch (method) {
1065 .blocked_canceling => if (std.Thread.use_pthreads) {1089 .sync => return switch (std.c.pthread_kill(thread.handle, .IO)) {
1066 return switch (std.c.pthread_kill(thread.handle, .IO)) {1090 0 => true,
1067 0 => true,1091 else => false,
1068 else => false,1092 },
1069 };1093 } else switch (native_os) {
1070 } else switch (native_os) {1094 .linux => switch (method) {
1071 .linux => {1095 .sync => {
1072 const pid: posix.pid_t = pid: {1096 const pid: posix.pid_t = pid: {
1073 const cached_pid = @atomicLoad(Pid, &t.pid, .monotonic);1097 const cached_pid = @atomicLoad(Pid, &t.pid, .monotonic);
1074 if (cached_pid != .unknown) break :pid @intFromEnum(cached_pid);1098 if (cached_pid != .unknown) break :pid @intFromEnum(cached_pid);
...@@ -1081,7 +1105,9 @@ const Thread = struct {...@@ -1081,7 +1105,9 @@ const Thread = struct {
1081 else => false,1105 else => false,
1082 };1106 };
1083 },1107 },
1084 .windows => {1108 },
1109 .windows => switch (method) {
1110 .sync => {
1085 var iosb: windows.IO_STATUS_BLOCK = undefined;1111 var iosb: windows.IO_STATUS_BLOCK = undefined;
1086 return switch (windows.ntdll.NtCancelSynchronousIoFile(thread.handle, null, &iosb)) {1112 return switch (windows.ntdll.NtCancelSynchronousIoFile(thread.handle, null, &iosb)) {
1087 .NOT_FOUND => true, // this might mean the operation hasn't started yet1113 .NOT_FOUND => true, // this might mean the operation hasn't started yet
...@@ -1089,15 +1115,8 @@ const Thread = struct {...@@ -1089,15 +1115,8 @@ const Thread = struct {
1089 else => false,1115 else => false,
1090 };1116 };
1091 },1117 },
1092 else => return false,1118 .dns => @panic("TODO call GetAddrInfoExCancel"),
1093 },1119 .apc => @panic("TODO call NtCancelIoFileEx"),
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 };
1101 },1120 },
11021121
1103 else => {1122 else => {
...@@ -1145,8 +1164,8 @@ const Syscall = struct {...@@ -1145,8 +1164,8 @@ const Syscall = struct {
1145 }, .monotonic).cancelation) {1164 }, .monotonic).cancelation) {
1146 .parked => unreachable,1165 .parked => unreachable,
1147 .blocked => unreachable,1166 .blocked => unreachable,
1148 .blocked_alertable => unreachable,1167 .blocked_apc => unreachable,
1149 .blocked_alertable_canceling => unreachable,1168 .blocked_windows_dns => unreachable,
1150 .blocked_canceling => unreachable,1169 .blocked_canceling => unreachable,
1151 .none => return .{ .thread = thread }, // new status is `.blocked`1170 .none => return .{ .thread = thread }, // new status is `.blocked`
1152 .canceling => return error.Canceled, // new status is `.canceled`1171 .canceling => return error.Canceled, // new status is `.canceled`
...@@ -1165,19 +1184,14 @@ const Syscall = struct {...@@ -1165,19 +1184,14 @@ const Syscall = struct {
1165 }, .monotonic).cancelation) {1184 }, .monotonic).cancelation) {
1166 .none => unreachable,1185 .none => unreachable,
1167 .parked => unreachable,1186 .parked => unreachable,
1168 .blocked_alertable => unreachable,1187 .blocked_apc => unreachable,
1169 .blocked_alertable_canceling => unreachable,1188 .blocked_windows_dns => unreachable,
1170 .canceling => unreachable,1189 .canceling => unreachable,
1171 .canceled => unreachable,1190 .canceled => unreachable,
1172 .blocked => {}, // new status is `.blocked` (unchanged)1191 .blocked => {}, // new status is `.blocked` (unchanged)
1173 .blocked_canceling => return error.Canceled, // new status is `.canceled`1192 .blocked_canceling => return error.Canceled, // new status is `.canceled`
1174 }1193 }
1175 }1194 }
1176 fn toApc(s: Syscall) Io.Cancelable!void {
1177 // TODO set state to indicate instead of NtCancelSynchronousIoFile we
1178 // need to use NtCancelIoFileEx
1179 return s.checkCancel();
1180 }
1181 /// Marks this syscall as finished.1195 /// Marks this syscall as finished.
1182 fn finish(s: Syscall) void {1196 fn finish(s: Syscall) void {
1183 const thread = s.thread orelse return;1197 const thread = s.thread orelse return;
...@@ -1187,8 +1201,8 @@ const Syscall = struct {...@@ -1187,8 +1201,8 @@ const Syscall = struct {
1187 }, .monotonic).cancelation) {1201 }, .monotonic).cancelation) {
1188 .none => unreachable,1202 .none => unreachable,
1189 .parked => unreachable,1203 .parked => unreachable,
1190 .blocked_alertable => unreachable,1204 .blocked_apc => unreachable,
1191 .blocked_alertable_canceling => unreachable,1205 .blocked_windows_dns => unreachable,
1192 .canceling => unreachable,1206 .canceling => unreachable,
1193 .canceled => unreachable,1207 .canceled => unreachable,
1194 .blocked => {}, // new status is `.none`1208 .blocked => {}, // new status is `.none`
...@@ -1196,25 +1210,25 @@ const Syscall = struct {...@@ -1196,25 +1210,25 @@ const Syscall = struct {
1196 }1210 }
1197 }1211 }
1198 /// Indicates instead of `NtCancelSynchronousIoFile` we need to use1212 /// Indicates instead of `NtCancelSynchronousIoFile` we need to use
1199 /// `NtAlertThread` to interrupt the wait.1213 /// `NtCancelIoFileEx` to interrupt the wait.
1200 ///1214 ///
1201 /// Windows only, called from blocked state only.1215 /// Windows only, called from blocked state only.
1202 fn toAlertable(s: Syscall) Io.Cancelable!AlertableSyscall {1216 fn toApc(s: Syscall, apc_context: ?*anyopaque) Io.Cancelable!void {
1203 comptime assert(is_windows);1217 const thread = s.thread orelse return;
1204 const thread = s.thread orelse return .{ .thread = null };1218 thread.apc_context = apc_context;
1205 var prev = thread.status.load(.monotonic);1219 var prev = thread.status.load(.monotonic);
1206 while (true) prev = switch (prev.cancelation) {1220 while (true) prev = switch (prev.cancelation) {
1207 .none => unreachable,1221 .none => unreachable,
1208 .parked => unreachable,1222 .parked => unreachable,
1209 .blocked_alertable => unreachable,1223 .blocked_apc => unreachable,
1210 .blocked_alertable_canceling => unreachable,1224 .blocked_windows_dns => unreachable,
1211 .canceling => unreachable,1225 .canceling => unreachable,
1212 .canceled => unreachable,1226 .canceled => unreachable,
12131227
1214 .blocked => thread.status.cmpxchgWeak(prev, .{1228 .blocked => thread.status.cmpxchgWeak(prev, .{
1215 .cancelation = .blocked_alertable,1229 .cancelation = .blocked_apc,
1216 .awaitable = prev.awaitable,1230 .awaitable = prev.awaitable,
1217 }, .monotonic, .monotonic) orelse return .{ .thread = thread },1231 }, .monotonic, .monotonic) orelse return,
12181232
1219 .blocked_canceling => thread.status.cmpxchgWeak(prev, .{1233 .blocked_canceling => thread.status.cmpxchgWeak(prev, .{
1220 .cancelation = .canceled,1234 .cancelation = .canceled,
...@@ -1222,6 +1236,45 @@ const Syscall = struct {...@@ -1222,6 +1236,45 @@ const Syscall = struct {
1222 }, .monotonic, .monotonic) orelse return error.Canceled,1236 }, .monotonic, .monotonic) orelse return error.Canceled,
1223 };1237 };
1224 }1238 }
1239 /// Windows only, called from blocked_apc state only.
1240 fn checkCancelApc(s: Syscall) Io.Cancelable!void {
1241 const thread = s.thread orelse return;
1242 var prev = thread.status.load(.monotonic);
1243 while (true) prev = switch (prev.cancelation) {
1244 .none => unreachable,
1245 .parked => unreachable,
1246 .blocked_windows_dns => unreachable,
1247 .blocked => unreachable,
1248 .canceling => unreachable,
1249 .canceled => unreachable,
1250 .blocked_apc => return,
1251 .blocked_canceling => thread.status.cmpxchgWeak(prev, .{
1252 .cancelation = .canceled,
1253 .awaitable = prev.awaitable,
1254 }, .monotonic, .monotonic) orelse return error.Canceled,
1255 };
1256 }
1257 /// Windows only, called from blocked_apc state only.
1258 fn finishApc(s: Syscall) void {
1259 const thread = s.thread orelse return;
1260 var prev = thread.status.load(.monotonic);
1261 while (true) prev = switch (prev.cancelation) {
1262 .none => unreachable,
1263 .parked => unreachable,
1264 .blocked_windows_dns => unreachable,
1265 .blocked => unreachable,
1266 .canceling => unreachable,
1267 .canceled => unreachable,
1268 .blocked_apc => thread.status.cmpxchgWeak(prev, .{
1269 .cancelation = .none,
1270 .awaitable = prev.awaitable,
1271 }, .monotonic, .monotonic) orelse return,
1272 .blocked_canceling => thread.status.cmpxchgWeak(prev, .{
1273 .cancelation = .canceling,
1274 .awaitable = prev.awaitable,
1275 }, .monotonic, .monotonic) orelse return,
1276 };
1277 }
1225 /// Convenience wrapper which calls `finish`, then returns `err`.1278 /// Convenience wrapper which calls `finish`, then returns `err`.
1226 fn fail(s: Syscall, err: anytype) @TypeOf(err) {1279 fn fail(s: Syscall, err: anytype) @TypeOf(err) {
1227 s.finish();1280 s.finish();
...@@ -1501,6 +1554,8 @@ fn worker(t: *Threaded) void {...@@ -1501,6 +1554,8 @@ fn worker(t: *Threaded) void {
1501 .cancel_protection = .unblocked,1554 .cancel_protection = .unblocked,
1502 .futex_waiter = undefined,1555 .futex_waiter = undefined,
1503 .csprng = .{},1556 .csprng = .{},
1557 .apc_context = undefined,
1558 .interrupt_method = undefined,
1504 };1559 };
1505 Thread.current = &thread;1560 Thread.current = &thread;
15061561
...@@ -2105,8 +2160,8 @@ fn groupAsyncEager(...@@ -2105,8 +2160,8 @@ fn groupAsyncEager(
2105 .canceled => true,2160 .canceled => true,
2106 .parked => unreachable,2161 .parked => unreachable,
2107 .blocked => unreachable,2162 .blocked => unreachable,
2108 .blocked_alertable => unreachable,2163 .blocked_apc => unreachable,
2109 .blocked_alertable_canceling => unreachable,2164 .blocked_windows_dns => unreachable,
2110 .blocked_canceling => unreachable,2165 .blocked_canceling => unreachable,
2111 };2166 };
2112 } else false;2167 } else false;
...@@ -2117,8 +2172,8 @@ fn groupAsyncEager(...@@ -2117,8 +2172,8 @@ fn groupAsyncEager(
2117 .canceled => true,2172 .canceled => true,
2118 .parked => unreachable,2173 .parked => unreachable,
2119 .blocked => unreachable,2174 .blocked => unreachable,
2120 .blocked_alertable => unreachable,2175 .blocked_apc => unreachable,
2121 .blocked_alertable_canceling => unreachable,2176 .blocked_windows_dns => unreachable,
2122 .blocked_canceling => unreachable,2177 .blocked_canceling => unreachable,
2123 };2178 };
2124 } else false;2179 } else false;
...@@ -2297,8 +2352,8 @@ fn recancelInner() void {...@@ -2297,8 +2352,8 @@ fn recancelInner() void {
2297 .canceling => unreachable, // called `recancel` but cancelation was already pending2352 .canceling => unreachable, // called `recancel` but cancelation was already pending
2298 .parked => unreachable,2353 .parked => unreachable,
2299 .blocked => unreachable,2354 .blocked => unreachable,
2300 .blocked_alertable => unreachable,2355 .blocked_apc => unreachable,
2301 .blocked_alertable_canceling => unreachable,2356 .blocked_windows_dns => unreachable,
2302 .blocked_canceling => unreachable,2357 .blocked_canceling => unreachable,
2303 }2358 }
2304}2359}
...@@ -8233,6 +8288,7 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us...@@ -8233,6 +8288,7 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us
8233 const buffer = data[index];8288 const buffer = data[index];
82348289
8235 var io_status_block: windows.IO_STATUS_BLOCK = undefined;8290 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
8291 var done: bool = false;
82368292
8237 read: {8293 read: {
8238 const syscall: Syscall = try .start();8294 const syscall: Syscall = try .start();
...@@ -8240,8 +8296,8 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us...@@ -8240,8 +8296,8 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us
8240 switch (windows.ntdll.NtReadFile(8296 switch (windows.ntdll.NtReadFile(
8241 file.handle,8297 file.handle,
8242 null, // event8298 null, // event
8243 noopApc, // apc callback8299 flagApc, // apc callback
8244 null, // apc context8300 &done, // apc context
8245 &io_status_block,8301 &io_status_block,
8246 buffer.ptr,8302 buffer.ptr,
8247 @min(std.math.maxInt(u32), buffer.len),8303 @min(std.math.maxInt(u32), buffer.len),
...@@ -8259,12 +8315,12 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us...@@ -8259,12 +8315,12 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us
8259 //else => |status| return syscall.unexpectedNtstatus(status),8315 //else => |status| return syscall.unexpectedNtstatus(status),
8260 }8316 }
8261 }8317 }
8262 try syscall.toApc();8318 try syscall.toApc(&done);
8263 while (true) {8319 while (true) {
8264 switch (windows.ntdll.NtDelayExecution(1, null)) {8320 switch (windows.ntdll.NtDelayExecution(1, null)) {
8265 .USER_APC => break syscall.finish(),8321 .USER_APC => break syscall.finishApc(),
8266 .SUCCESS, .CANCELLED => {8322 .SUCCESS, .CANCELLED => {
8267 try syscall.checkCancel();8323 try syscall.checkCancelApc();
8268 continue;8324 continue;
8269 },8325 },
8270 else => |status| std.debug.panic("fileReadStreamingWindows NtDelayExecution returned {t}", .{status}),8326 else => |status| std.debug.panic("fileReadStreamingWindows NtDelayExecution returned {t}", .{status}),
...@@ -8282,12 +8338,13 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us...@@ -8282,12 +8338,13 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us
8282 return io_status_block.Information;8338 return io_status_block.Information;
8283}8339}
82848340
8285fn noopApc(8341fn flagApc(
8286 apc_context: ?*anyopaque,8342 apc_context: ?*anyopaque,
8287 io_status_block: *windows.IO_STATUS_BLOCK,8343 io_status_block: *windows.IO_STATUS_BLOCK,
8288 unused: windows.ULONG,8344 unused: windows.ULONG,
8289) callconv(.winapi) void {8345) callconv(.winapi) void {
8290 _ = apc_context;8346 const flag: *bool = @ptrCast(apc_context);
8347 flag.* = true;
8291 _ = io_status_block;8348 _ = io_status_block;
8292 _ = unused;8349 _ = unused;
8293}8350}
...@@ -12254,7 +12311,7 @@ fn netLookupFallible(...@@ -12254,7 +12311,7 @@ fn netLookupFallible(
12254 var res: *ws2_32.ADDRINFOEXW = undefined;12311 var res: *ws2_32.ADDRINFOEXW = undefined;
12255 const timeout: ?*ws2_32.timeval = null;12312 const timeout: ?*ws2_32.timeval = null;
12256 while (true) {12313 while (true) {
12257 // TODO: hook this up to cancelation with `NtDelayExecution` and APC callbacks.12314 // TODO: hook this up to cancelation with `Thread.Status.cancelation.blocked_windows_dns`.
12258 try Thread.checkCancel();12315 try Thread.checkCancel();
12259 // TODO make this append to the queue eagerly rather than blocking until the whole thing finishes12316 // TODO make this append to the queue eagerly rather than blocking until the whole thing finishes
12260 const rc: ws2_32.WinsockError = @enumFromInt(ws2_32.GetAddrInfoExW(name_w, port_w, .DNS, null, &hints, &res, timeout, null, null, null));12317 const rc: ws2_32.WinsockError = @enumFromInt(ws2_32.GetAddrInfoExW(name_w, port_w, .DNS, null, &hints, &res, timeout, null, null, null));
...@@ -15937,8 +15994,8 @@ const parking_futex = struct {...@@ -15937,8 +15994,8 @@ const parking_futex = struct {
15937 .canceled => break :cancelable, // status is still `.canceled`15994 .canceled => break :cancelable, // status is still `.canceled`
15938 .parked => unreachable,15995 .parked => unreachable,
15939 .blocked => unreachable,15996 .blocked => unreachable,
15940 .blocked_alertable => unreachable,15997 .blocked_apc => unreachable,
15941 .blocked_alertable_canceling => unreachable,15998 .blocked_windows_dns => unreachable,
15942 .blocked_canceling => unreachable,15999 .blocked_canceling => unreachable,
15943 }16000 }
15944 // We could now be unparked for a cancelation at any time!16001 // We could now be unparked for a cancelation at any time!
...@@ -15989,8 +16046,8 @@ const parking_futex = struct {...@@ -15989,8 +16046,8 @@ const parking_futex = struct {
15989 },16046 },
15990 .canceled => unreachable,16047 .canceled => unreachable,
15991 .blocked => unreachable,16048 .blocked => unreachable,
15992 .blocked_alertable => unreachable,16049 .blocked_apc => unreachable,
15993 .blocked_alertable_canceling => unreachable,16050 .blocked_windows_dns => unreachable,
15994 .blocked_canceling => unreachable,16051 .blocked_canceling => unreachable,
15995 },16052 },
15996 }16053 }
...@@ -16031,8 +16088,8 @@ const parking_futex = struct {...@@ -16031,8 +16088,8 @@ const parking_futex = struct {
16031 .canceling => continue, // race with a canceler who hasn't called `removeCanceledWaiter` yet16088 .canceling => continue, // race with a canceler who hasn't called `removeCanceledWaiter` yet
16032 .canceled => unreachable,16089 .canceled => unreachable,
16033 .blocked => unreachable,16090 .blocked => unreachable,
16034 .blocked_alertable => unreachable,16091 .blocked_apc => unreachable,
16035 .blocked_alertable_canceling => unreachable,16092 .blocked_windows_dns => unreachable,
16036 .blocked_canceling => unreachable,16093 .blocked_canceling => unreachable,
16037 }16094 }
16038 // We're waking this waiter. Remove them from the bucket and add them to our local list.16095 // We're waking this waiter. Remove them from the bucket and add them to our local list.
...@@ -16098,8 +16155,8 @@ const parking_sleep = struct {...@@ -16098,8 +16155,8 @@ const parking_sleep = struct {
16098 .canceled => break :cancelable, // status is still `.canceled`16155 .canceled => break :cancelable, // status is still `.canceled`
16099 .parked => unreachable,16156 .parked => unreachable,
16100 .blocked => unreachable,16157 .blocked => unreachable,
16101 .blocked_alertable => unreachable,16158 .blocked_apc => unreachable,
16102 .blocked_alertable_canceling => unreachable,16159 .blocked_windows_dns => unreachable,
16103 .blocked_canceling => unreachable,16160 .blocked_canceling => unreachable,
16104 }16161 }
16105 while (park(deadline, null)) {16162 while (park(deadline, null)) {
...@@ -16117,8 +16174,8 @@ const parking_sleep = struct {...@@ -16117,8 +16174,8 @@ const parking_sleep = struct {
16117 .none => unreachable,16174 .none => unreachable,
16118 .canceled => unreachable,16175 .canceled => unreachable,
16119 .blocked => unreachable,16176 .blocked => unreachable,
16120 .blocked_alertable => unreachable,16177 .blocked_apc => unreachable,
16121 .blocked_alertable_canceling => unreachable,16178 .blocked_windows_dns => unreachable,
16122 .blocked_canceling => unreachable,16179 .blocked_canceling => unreachable,
16123 }16180 }
16124 } else |err| switch (err) {16181 } else |err| switch (err) {
...@@ -16137,8 +16194,8 @@ const parking_sleep = struct {...@@ -16137,8 +16194,8 @@ const parking_sleep = struct {
16137 .none => unreachable,16194 .none => unreachable,
16138 .canceled => unreachable,16195 .canceled => unreachable,
16139 .blocked => unreachable,16196 .blocked => unreachable,
16140 .blocked_alertable => unreachable,16197 .blocked_apc => unreachable,
16141 .blocked_alertable_canceling => unreachable,16198 .blocked_windows_dns => unreachable,
16142 .blocked_canceling => unreachable,16199 .blocked_canceling => unreachable,
16143 },16200 },
16144 }16201 }