| ... | @@ -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` values | 395 | 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 results | 588 | 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, |
| 628 | | 638 | |
| 629 | csprng: Csprng, | 639 | csprng: Csprng, |
| 630 | | 640 | |
| ... | @@ -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, |
| 654 | | 664 | |
| 655 | /// Windows-only: the thread is blocked in an alertable wait via | 665 | /// Windows-only: the thread is blocked in a call to `NtDelayExecution`. |
| 656 | /// `NtDelayExecution`. To request cancelation, set the status to | 666 | /// To request cancelation, set the status to `.canceling` and call `NtCancelIoFileEx`. |
| 657 | /// `blocked_alertable_canceling` and repeatedly alert the thread | 667 | 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, |
| 660 | | 672 | |
| 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. In | 998 | /// 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 will | 999 | /// 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 else | 1004 | 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, |
| 1000 | | 1012 | |
| 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 | }, |
| 1014 | | 1026 | |
| 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 | }, |
| 1021 | | 1043 | |
| 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 | }, |
| 1031 | | 1053 | |
| 1032 | .canceling, .canceled => { | 1054 | .canceling, .canceled => { |
| 1033 | // This can happen when the task start raced with the cancelation, so the thread | 1055 | // 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 the | 1056 | // 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 | }, |
| 1038 | | 1060 | |
| 1039 | .blocked_canceling => unreachable, // `awaitable` has not been canceled before now | 1061 | .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 | } |
| 1044 | | 1066 | |
| | 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 observed | 1072 | /// 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` and | 1078 | /// the thread is still blocked. For the implementation, `Future.waitForCancelWithSignaling` and |
| 1052 | /// `Group.waitForCancelWithSignaling`: they use exponential backoff starting at a 1us delay and | 1079 | /// `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 | } | | |
| 1060 | | 1084 | |
| 1061 | // The thread ID and/or handle can be read non-atomically because they never change and were | 1085 | // 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. |
| 1063 | | 1087 | |
| 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 yet | 1113 | .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 | }, |
| 1102 | | 1121 | |
| 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 use | 1212 | /// 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, |
| 1213 | | 1227 | |
| 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, |
| 1218 | | 1232 | |
| 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; |
| 1506 | | 1561 | |
| ... | @@ -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 pending | 2352 | .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]; |
| 8234 | | 8289 | |
| 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; |
| 8236 | | 8292 | |
| 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, // event | 8298 | null, // event |
| 8243 | noopApc, // apc callback | 8299 | flagApc, // apc callback |
| 8244 | null, // apc context | 8300 | &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 | } |
| 8284 | | 8340 | |
| 8285 | fn noopApc( | 8341 | fn 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 finishes | 12316 | // 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` yet | 16088 | .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 | } |