authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-21 21:28:15-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-30 12:10:01-08:00
log36940c39539e5d4d8a39ceaed035f213b3902c92
tree6da934b945e0f58d79180bbe9d21db5ad98e6c0e
parent0553525fe6679a1d9a1b67e5f9cec74876379b64

std.Io.Threaded: implement APC cancelation

specifically the call to NtCancelIoFileEx

2 files changed, 29 insertions(+), 10 deletions(-)

lib/std/Io/Threaded.zig+26-7
......@@ -632,12 +632,17 @@ const Thread = struct {
632632 cancel_protection: Io.CancelProtection,
633633 /// Always released when `Status.cancelation` is set to `.parked`.
634634 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,
635 apc_context: if (is_windows) ?*anyopaque else void,
635 apc: Apc,
636636 /// Used only by group cancelation code for temporary storage.
637637 interrupt_method: InterruptMethod,
638638
639639 csprng: Csprng,
640640
641 const Apc = if (is_windows) struct {
642 handle: windows.HANDLE,
643 iosb: ?*windows.IO_STATUS_BLOCK,
644 } else void;
645
641646 const Handle = Handle: {
642647 if (std.Thread.use_pthreads) break :Handle std.c.pthread_t;
643648 if (is_windows) break :Handle windows.HANDLE;
......@@ -1116,7 +1121,14 @@ const Thread = struct {
11161121 };
11171122 },
11181123 .dns => @panic("TODO call GetAddrInfoExCancel"),
1119 .apc => @panic("TODO call NtCancelIoFileEx"),
1124 .apc => {
1125 var iosb: windows.IO_STATUS_BLOCK = undefined;
1126 return switch (windows.ntdll.NtCancelIoFileEx(thread.apc.handle, thread.apc.iosb, &iosb)) {
1127 .NOT_FOUND => true, // this might mean the operation hasn't started yet
1128 .SUCCESS => false, // the OS confirmed that our cancelation worked
1129 else => false,
1130 };
1131 },
11201132 },
11211133
11221134 else => {
......@@ -1213,9 +1225,9 @@ const Syscall = struct {
12131225 /// `NtCancelIoFileEx` to interrupt the wait.
12141226 ///
12151227 /// Windows only, called from blocked state only.
1216 fn toApc(s: Syscall, apc_context: ?*anyopaque) Io.Cancelable!void {
1228 fn toApc(s: Syscall, apc: Thread.Apc) Io.Cancelable!void {
12171229 const thread = s.thread orelse return;
1218 thread.apc_context = apc_context;
1230 thread.apc = apc;
12191231 var prev = thread.status.load(.monotonic);
12201232 while (true) prev = switch (prev.cancelation) {
12211233 .none => unreachable,
......@@ -1554,7 +1566,7 @@ fn worker(t: *Threaded) void {
15541566 .cancel_protection = .unblocked,
15551567 .futex_waiter = undefined,
15561568 .csprng = .{},
1557 .apc_context = undefined,
1569 .apc = undefined,
15581570 .interrupt_method = undefined,
15591571 };
15601572 Thread.current = &thread;
......@@ -8458,10 +8470,17 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us
84588470 //else => |status| return syscall.unexpectedNtstatus(status),
84598471 }
84608472 }
8461 try syscall.toApc(&done);
8473 try syscall.toApc(.{ .handle = file.handle, .iosb = &io_status_block });
84628474 while (true) {
84638475 switch (windows.ntdll.NtDelayExecution(1, null)) {
8464 .USER_APC => break syscall.finishApc(),
8476 .USER_APC => {
8477 if (!done) {
8478 // Other APC work was queued before calling into this function.
8479 try syscall.checkCancelApc();
8480 continue;
8481 }
8482 break syscall.finishApc();
8483 },
84658484 .SUCCESS, .CANCELLED => {
84668485 try syscall.checkCancelApc();
84678486 continue;
lib/std/os/windows/ntdll.zig+3-3
......@@ -601,11 +601,11 @@ pub extern "ntdll" fn NtDelayExecution(
601601
602602pub extern "ntdll" fn NtCancelIoFileEx(
603603 FileHandle: HANDLE,
604 IoRequestToCancel: *const IO_STATUS_BLOCK,
604 IoRequestToCancel: ?*IO_STATUS_BLOCK,
605605 IoStatusBlock: *IO_STATUS_BLOCK,
606606) callconv(.winapi) NTSTATUS;
607607
608608pub extern "ntdll" fn NtCancelIoFile(
609 handle: HANDLE,
610 iosbToCancel: *const IO_STATUS_BLOCK,
609 FileHandle: HANDLE,
610 IoRequestToCancel: ?*IO_STATUS_BLOCK,
611611) callconv(.winapi) NTSTATUS;