| ... | @@ -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` values | 392 | 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 results | 581 | 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, | | |
| 638 | | 628 | |
| 639 | csprng: Csprng, | 629 | csprng: Csprng, |
| 640 | | 630 | |
| 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, |
| 669 | | 654 | |
| 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 |
| 673 | | 658 | /// 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, | | |
| 677 | | 660 | |
| 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. In | 986 | /// 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 will | 987 | /// 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 else | 992 | 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, |
| 1017 | | 1000 | |
| 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 | }, |
| 1031 | | 1014 | |
| 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 | }, | | |
| 1048 | | 1021 | |
| 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 | }, |
| 1058 | | 1031 | |
| 1059 | .canceling, .canceled => { | 1032 | .canceling, .canceled => { |
| 1060 | // This can happen when the task start raced with the cancelation, so the thread | 1033 | // 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 the | 1034 | // 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 | }, |
| 1065 | | 1038 | |
| 1066 | .blocked_canceling => unreachable, // `awaitable` has not been canceled before now | 1039 | .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 | } |
| 1071 | | 1044 | |
| 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 observed | 1045 | /// 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` and | 1051 | /// the thread is still blocked. For the implementation, `Future.waitForCancelWithSignaling` and |
| 1084 | /// `Group.waitForCancelWithSignaling`: they use exponential backoff starting at a 1us delay and | 1052 | /// `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 | } |
| 1089 | | 1060 | |
| 1090 | // The thread ID and/or handle can be read non-atomically because they never change and were | 1061 | // 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. |
| 1092 | | 1063 | |
| 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 yet | 1087 | .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 yet | 1096 | if (!is_windows) unreachable; |
| 1128 | .SUCCESS => false, // the OS confirmed that our cancelation worked | 1097 | return switch (windows.ntdll.NtAlertThread(thread.handle)) { |
| 1129 | else => false, | 1098 | .SUCCESS => true, |
| 1130 | }; | 1099 | else => false, |
| 1131 | }, | 1100 | }; |
| 1132 | }, | 1101 | }, |
| 1133 | | 1102 | |
| 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 use | 1193 | /// 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, |
| 1239 | | 1208 | |
| 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 }, |
| 1244 | | 1213 | |
| 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; |
| 1573 | | 1501 | |
| ... | @@ -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 pending | 2296 | .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 file | 8397 | .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 | } |
| 8494 | | 8424 | |
| 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 finishes | 12453 | // 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` yet | 16276 | .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 | } |