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 {...@@ -632,12 +632,17 @@ const Thread = struct {
632 cancel_protection: Io.CancelProtection,632 cancel_protection: Io.CancelProtection,
633 /// Always released when `Status.cancelation` is set to `.parked`.633 /// Always released when `Status.cancelation` is set to `.parked`.
634 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,635 apc: Apc,
636 /// Used only by group cancelation code for temporary storage.636 /// Used only by group cancelation code for temporary storage.
637 interrupt_method: InterruptMethod,637 interrupt_method: InterruptMethod,
638638
639 csprng: Csprng,639 csprng: Csprng,
640640
641 const Apc = if (is_windows) struct {
642 handle: windows.HANDLE,
643 iosb: ?*windows.IO_STATUS_BLOCK,
644 } else void;
645
641 const Handle = Handle: {646 const Handle = Handle: {
642 if (std.Thread.use_pthreads) break :Handle std.c.pthread_t;647 if (std.Thread.use_pthreads) break :Handle std.c.pthread_t;
643 if (is_windows) break :Handle windows.HANDLE;648 if (is_windows) break :Handle windows.HANDLE;
...@@ -1116,7 +1121,14 @@ const Thread = struct {...@@ -1116,7 +1121,14 @@ const Thread = struct {
1116 };1121 };
1117 },1122 },
1118 .dns => @panic("TODO call GetAddrInfoExCancel"),1123 .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 },
1120 },1132 },
11211133
1122 else => {1134 else => {
...@@ -1213,9 +1225,9 @@ const Syscall = struct {...@@ -1213,9 +1225,9 @@ const Syscall = struct {
1213 /// `NtCancelIoFileEx` to interrupt the wait.1225 /// `NtCancelIoFileEx` to interrupt the wait.
1214 ///1226 ///
1215 /// Windows only, called from blocked state only.1227 /// 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 {
1217 const thread = s.thread orelse return;1229 const thread = s.thread orelse return;
1218 thread.apc_context = apc_context;1230 thread.apc = apc;
1219 var prev = thread.status.load(.monotonic);1231 var prev = thread.status.load(.monotonic);
1220 while (true) prev = switch (prev.cancelation) {1232 while (true) prev = switch (prev.cancelation) {
1221 .none => unreachable,1233 .none => unreachable,
...@@ -1554,7 +1566,7 @@ fn worker(t: *Threaded) void {...@@ -1554,7 +1566,7 @@ fn worker(t: *Threaded) void {
1554 .cancel_protection = .unblocked,1566 .cancel_protection = .unblocked,
1555 .futex_waiter = undefined,1567 .futex_waiter = undefined,
1556 .csprng = .{},1568 .csprng = .{},
1557 .apc_context = undefined,1569 .apc = undefined,
1558 .interrupt_method = undefined,1570 .interrupt_method = undefined,
1559 };1571 };
1560 Thread.current = &thread;1572 Thread.current = &thread;
...@@ -8315,10 +8327,17 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us...@@ -8315,10 +8327,17 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us
8315 //else => |status| return syscall.unexpectedNtstatus(status),8327 //else => |status| return syscall.unexpectedNtstatus(status),
8316 }8328 }
8317 }8329 }
8318 try syscall.toApc(&done);8330 try syscall.toApc(.{ .handle = file.handle, .iosb = &io_status_block });
8319 while (true) {8331 while (true) {
8320 switch (windows.ntdll.NtDelayExecution(1, null)) {8332 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 },
8322 .SUCCESS, .CANCELLED => {8341 .SUCCESS, .CANCELLED => {
8323 try syscall.checkCancelApc();8342 try syscall.checkCancelApc();
8324 continue;8343 continue;
lib/std/os/windows/ntdll.zig+3-3
...@@ -596,11 +596,11 @@ pub extern "ntdll" fn NtDelayExecution(...@@ -596,11 +596,11 @@ pub extern "ntdll" fn NtDelayExecution(
596596
597pub extern "ntdll" fn NtCancelIoFileEx(597pub extern "ntdll" fn NtCancelIoFileEx(
598 FileHandle: HANDLE,598 FileHandle: HANDLE,
599 IoRequestToCancel: *const IO_STATUS_BLOCK,599 IoRequestToCancel: ?*IO_STATUS_BLOCK,
600 IoStatusBlock: *IO_STATUS_BLOCK,600 IoStatusBlock: *IO_STATUS_BLOCK,
601) callconv(.winapi) NTSTATUS;601) callconv(.winapi) NTSTATUS;
602602
603pub extern "ntdll" fn NtCancelIoFile(603pub extern "ntdll" fn NtCancelIoFile(
604 handle: HANDLE,604 FileHandle: HANDLE,
605 iosbToCancel: *const IO_STATUS_BLOCK,605 IoRequestToCancel: ?*IO_STATUS_BLOCK,
606) callconv(.winapi) NTSTATUS;606) callconv(.winapi) NTSTATUS;