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-27 15:32:34-08:00
log7ab41bec2933ffb2ce136779fbc8c22c45df28a8
tree76e70fda50171238780a6640ae71cdd9b4fb2ffa
parente18ee3a93afb7cb090ac6ef2cd49d2386edd3950

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;
......@@ -8315,10 +8327,17 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us
83158327 //else => |status| return syscall.unexpectedNtstatus(status),
83168328 }
83178329 }
8318 try syscall.toApc(&done);
8330 try syscall.toApc(.{ .handle = file.handle, .iosb = &io_status_block });
83198331 while (true) {
83208332 switch (windows.ntdll.NtDelayExecution(1, null)) {
8321 .USER_APC => break syscall.finishApc(),
8333 .USER_APC => {
8334 if (!done) {
8335 // Other APC work was queued before calling into this function.
8336 try syscall.checkCancelApc();
8337 continue;
8338 }
8339 break syscall.finishApc();
8340 },
83228341 .SUCCESS, .CANCELLED => {
83238342 try syscall.checkCancelApc();
83248343 continue;
lib/std/os/windows/ntdll.zig+3-3
......@@ -596,11 +596,11 @@ pub extern "ntdll" fn NtDelayExecution(
596596
597597pub extern "ntdll" fn NtCancelIoFileEx(
598598 FileHandle: HANDLE,
599 IoRequestToCancel: *const IO_STATUS_BLOCK,
599 IoRequestToCancel: ?*IO_STATUS_BLOCK,
600600 IoStatusBlock: *IO_STATUS_BLOCK,
601601) callconv(.winapi) NTSTATUS;
602602
603603pub extern "ntdll" fn NtCancelIoFile(
604 handle: HANDLE,
605 iosbToCancel: *const IO_STATUS_BLOCK,
604 FileHandle: HANDLE,
605 IoRequestToCancel: ?*IO_STATUS_BLOCK,
606606) callconv(.winapi) NTSTATUS;