authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-03 15:40:24-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-04 00:27:09-08:00
logbe977e1934c39fd276bd67e7da718a12a6e0f668
tree1560dab0b66b841c05f3a4f11f42eb711f3bc2cd
parentff67f70cf983ce772b0b4ef4a891bca1a51781c4

std.Io.Threaded: integrate with new cancel mechanism


2 files changed, 75 insertions(+), 38 deletions(-)

lib/std/Io/Threaded.zig+71-37
...@@ -1188,7 +1188,6 @@ pub fn init(...@@ -1188,7 +1188,6 @@ pub fn init(
1188 .argv0 = options.argv0,1188 .argv0 = options.argv0,
1189 .worker_threads = .init(null),1189 .worker_threads = .init(null),
1190 .environ = .{ .process_environ = options.environ },1190 .environ = .{ .process_environ = options.environ },
1191 .robust_cancel = options.robust_cancel,
1192 };1191 };
11931192
1194 if (posix.Sigaction != void) {1193 if (posix.Sigaction != void) {
...@@ -13050,9 +13049,10 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce...@@ -13050,9 +13049,10 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce
13050fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitError!process.Child.Term {13049fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitError!process.Child.Term {
13051 if (native_os == .wasi) unreachable;13050 if (native_os == .wasi) unreachable;
13052 const t: *Threaded = @ptrCast(@alignCast(userdata));13051 const t: *Threaded = @ptrCast(@alignCast(userdata));
13052 _ = t;
13053 switch (native_os) {13053 switch (native_os) {
13054 .windows => return childWaitWindows(t, child),13054 .windows => return childWaitWindows(child),
13055 else => return childWaitPosix(Thread.getCurrent(t), child),13055 else => return childWaitPosix(child),
13056 }13056 }
13057}13057}
1305813058
...@@ -13062,7 +13062,8 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void {...@@ -13062,7 +13062,8 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void {
13062 if (is_windows) {13062 if (is_windows) {
13063 childKillWindows(t, child, 1) catch childCleanupWindows(child);13063 childKillWindows(t, child, 1) catch childCleanupWindows(child);
13064 } else {13064 } else {
13065 childKillPosix(Thread.getCurrent(t), child) catch childCleanupPosix(child);13065 childKillPosix(child) catch {};
13066 childCleanupPosix(child);
13066 }13067 }
13067}13068}
1306813069
...@@ -13087,21 +13088,24 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT...@@ -13087,21 +13088,24 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT
13087 childCleanupWindows(child);13088 childCleanupWindows(child);
13088}13089}
1308913090
13090fn childWaitWindows(t: *Threaded, child: *process.Child) process.Child.WaitError!process.Child.Term {13091fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term {
13091 const current_thread = Thread.getCurrent(t);
13092 const handle = child.id.?;13092 const handle = child.id.?;
1309313093
13094 while (true) {13094 var syscall: Syscall = try .start();
13095 try current_thread.checkCancel();13095 while (true) switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) {
13096 switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) {13096 windows.WAIT_OBJECT_0 => break syscall.finish(),
13097 windows.WAIT_OBJECT_0 => break,13097 windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => {
13098 windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => continue,13098 try syscall.checkCancel();
13099 windows.WAIT_FAILED => switch (windows.GetLastError()) {13099 continue;
13100 },
13101 windows.WAIT_FAILED => {
13102 syscall.finish();
13103 switch (windows.GetLastError()) {
13100 else => |err| return windows.unexpectedError(err),13104 else => |err| return windows.unexpectedError(err),
13101 },13105 }
13102 else => return error.Unexpected,13106 },
13103 }13107 else => return syscall.fail(error.Unexpected),
13104 }13108 };
1310513109
13106 const term: process.Child.Term = x: {13110 const term: process.Child.Term = x: {
13107 var exit_code: windows.DWORD = undefined;13111 var exit_code: windows.DWORD = undefined;
...@@ -13142,7 +13146,7 @@ fn childCleanupWindows(child: *process.Child) void {...@@ -13142,7 +13146,7 @@ fn childCleanupWindows(child: *process.Child) void {
13142 }13146 }
13143}13147}
1314413148
13145fn childWaitPosix(current_thread: *Thread, child: *process.Child) process.Child.WaitError!process.Child.Term {13149fn childWaitPosix(child: *process.Child) process.Child.WaitError!process.Child.Term {
13146 defer childCleanupPosix(child);13150 defer childCleanupPosix(child);
1314713151
13148 const pid = child.id.?;13152 const pid = child.id.?;
...@@ -13152,29 +13156,29 @@ fn childWaitPosix(current_thread: *Thread, child: *process.Child) process.Child....@@ -13152,29 +13156,29 @@ fn childWaitPosix(current_thread: *Thread, child: *process.Child) process.Child.
1315213156
13153 if (have_wait4) {13157 if (have_wait4) {
13154 var status: if (builtin.link_libc) c_int else u32 = undefined;13158 var status: if (builtin.link_libc) c_int else u32 = undefined;
13155 try current_thread.beginSyscall();13159 const syscall: Syscall = try .start();
13156 while (true) switch (posix.errno(posix.system.wait4(pid, &status, 0, ru_ptr))) {13160 while (true) switch (posix.errno(posix.system.wait4(pid, &status, 0, ru_ptr))) {
13157 .SUCCESS => {13161 .SUCCESS => {
13158 current_thread.endSyscall();13162 syscall.finish();
13159 if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*;13163 if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*;
13160 return statusToTerm(@bitCast(status));13164 return statusToTerm(@bitCast(status));
13161 },13165 },
13162 .INTR => {13166 .INTR => {
13163 try current_thread.checkCancel();13167 try syscall.checkCancel();
13164 continue;13168 continue;
13165 },13169 },
13166 .CHILD => |err| return current_thread.endSyscallErrnoBug(err), // Double-free.13170 .CHILD => |err| return syscall.errnoBug(err), // Double-free.
13167 else => |err| return current_thread.endSyscallUnexpectedErrno(err),13171 else => |err| return syscall.unexpectedErrno(err),
13168 };13172 };
13169 }13173 }
1317013174
13171 if (have_waitid) {13175 if (have_waitid) {
13172 const linux = std.os.linux; // Bypass libc which has the wrong signature.13176 const linux = std.os.linux; // Bypass libc which has the wrong signature.
13173 var info: linux.siginfo_t = undefined;13177 var info: linux.siginfo_t = undefined;
13174 try current_thread.beginSyscall();13178 const syscall: Syscall = try .start();
13175 while (true) switch (linux.errno(linux.waitid(.PID, pid, &info, linux.W.EXITED, ru_ptr))) {13179 while (true) switch (linux.errno(linux.waitid(.PID, pid, &info, linux.W.EXITED, ru_ptr))) {
13176 .SUCCESS => {13180 .SUCCESS => {
13177 current_thread.endSyscall();13181 syscall.finish();
13178 if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*;13182 if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*;
13179 const status: u32 = @bitCast(info.fields.common.second.sigchld.status);13183 const status: u32 = @bitCast(info.fields.common.second.sigchld.status);
13180 const code: linux.CLD = @enumFromInt(info.code);13184 const code: linux.CLD = @enumFromInt(info.code);
...@@ -13186,26 +13190,27 @@ fn childWaitPosix(current_thread: *Thread, child: *process.Child) process.Child....@@ -13186,26 +13190,27 @@ fn childWaitPosix(current_thread: *Thread, child: *process.Child) process.Child.
13186 };13190 };
13187 },13191 },
13188 .INTR => {13192 .INTR => {
13189 try current_thread.checkCancel();13193 try syscall.checkCancel();
13190 continue;13194 continue;
13191 },13195 },
13192 .CHILD => |err| return current_thread.endSyscallErrnoBug(err), // Double-free.13196 .CHILD => |err| return syscall.errnoBug(err), // Double-free.
13193 else => |err| return current_thread.endSyscallUnexpectedErrno(err),13197 else => |err| return syscall.unexpectedErrno(err),
13194 };13198 };
13195 }13199 }
1319613200
13197 var status: if (builtin.link_libc) c_int else u32 = undefined;13201 var status: if (builtin.link_libc) c_int else u32 = undefined;
13202 const syscall: Syscall = try .start();
13198 while (true) switch (posix.errno(posix.system.waitpid(pid, &status, 0))) {13203 while (true) switch (posix.errno(posix.system.waitpid(pid, &status, 0))) {
13199 .SUCCESS => {13204 .SUCCESS => {
13200 current_thread.endSyscall();13205 syscall.finish();
13201 return statusToTerm(@bitCast(status));13206 return statusToTerm(@bitCast(status));
13202 },13207 },
13203 .INTR => {13208 .INTR => {
13204 try current_thread.checkCancel();13209 try syscall.checkCancel();
13205 continue;13210 continue;
13206 },13211 },
13207 .CHILD => |err| return current_thread.endSyscallErrnoBug(err), // Double-free.13212 .CHILD => |err| return syscall.errnoBug(err), // Double-free.
13208 else => |err| return current_thread.endSyscallUnexpectedErrno(err),13213 else => |err| return syscall.unexpectedErrno(err),
13209 };13214 };
13210}13215}
1321113216
...@@ -13220,9 +13225,12 @@ fn statusToTerm(status: u32) process.Child.Term {...@@ -13220,9 +13225,12 @@ fn statusToTerm(status: u32) process.Child.Term {
13220 .{ .unknown = status };13225 .{ .unknown = status };
13221}13226}
1322213227
13223fn childKillPosix(current_thread: *Thread, child: *process.Child) !void {13228fn childKillPosix(child: *process.Child) !void {
13224 // Intentionally uncancelable.13229 // Entire function body is intentionally uncancelable.
13225 while (true) switch (posix.errno(posix.system.kill(child.id.?, .TERM))) {13230
13231 const pid = child.id.?;
13232
13233 while (true) switch (posix.errno(posix.system.kill(pid, .TERM))) {
13226 .SUCCESS => break,13234 .SUCCESS => break,
13227 .INTR => continue,13235 .INTR => continue,
13228 .PERM => return error.PermissionDenied,13236 .PERM => return error.PermissionDenied,
...@@ -13230,7 +13238,35 @@ fn childKillPosix(current_thread: *Thread, child: *process.Child) !void {...@@ -13230,7 +13238,35 @@ fn childKillPosix(current_thread: *Thread, child: *process.Child) !void {
13230 .SRCH => |err| return errnoBug(err),13238 .SRCH => |err| return errnoBug(err),
13231 else => |err| return posix.unexpectedErrno(err),13239 else => |err| return posix.unexpectedErrno(err),
13232 };13240 };
13233 _ = try childWaitPosix(current_thread, child);13241
13242 if (have_wait4) {
13243 var status: if (builtin.link_libc) c_int else u32 = undefined;
13244 while (true) switch (posix.errno(posix.system.wait4(pid, &status, 0, null))) {
13245 .SUCCESS => return,
13246 .INTR => continue,
13247 .CHILD => |err| return errnoBug(err), // Double-free.
13248 else => |err| return posix.unexpectedErrno(err),
13249 };
13250 }
13251
13252 if (have_waitid) {
13253 const linux = std.os.linux; // Bypass libc which has the wrong signature.
13254 var info: linux.siginfo_t = undefined;
13255 while (true) switch (linux.errno(linux.waitid(.PID, pid, &info, linux.W.EXITED, null))) {
13256 .SUCCESS => return,
13257 .INTR => continue,
13258 .CHILD => |err| return errnoBug(err), // Double-free.
13259 else => |err| return posix.unexpectedErrno(err),
13260 };
13261 }
13262
13263 var status: if (builtin.link_libc) c_int else u32 = undefined;
13264 while (true) switch (posix.errno(posix.system.waitpid(pid, &status, 0))) {
13265 .SUCCESS => return,
13266 .INTR => continue,
13267 .CHILD => |err| return errnoBug(err), // Double-free.
13268 else => |err| return posix.unexpectedErrno(err),
13269 };
13234}13270}
1323513271
13236fn childCleanupPosix(child: *process.Child) void {13272fn childCleanupPosix(child: *process.Child) void {
...@@ -13537,7 +13573,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro...@@ -13537,7 +13573,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
13537 }13573 }
1353813574
13539 windowsCreateProcessPathExt(13575 windowsCreateProcessPathExt(
13540 t,
13541 arena,13576 arena,
13542 &dir_buf,13577 &dir_buf,
13543 &app_buf,13578 &app_buf,
...@@ -13573,7 +13608,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro...@@ -13573,7 +13608,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
13573 try dir_buf.appendSlice(arena, search_path);13608 try dir_buf.appendSlice(arena, search_path);
1357413609
13575 if (windowsCreateProcessPathExt(13610 if (windowsCreateProcessPathExt(
13576 t,
13577 arena,13611 arena,
13578 &dir_buf,13612 &dir_buf,
13579 &app_buf,13613 &app_buf,
lib/std/Io/Threaded/test.zig+4-1
...@@ -170,7 +170,10 @@ test "cancel blocked read from pipe" {...@@ -170,7 +170,10 @@ test "cancel blocked read from pipe" {
170 }170 }
171 };171 };
172172
173 var threaded: std.Io.Threaded = .init(std.testing.allocator, .{});173 var threaded: std.Io.Threaded = .init(std.testing.allocator, .{
174 .argv0 = .empty,
175 .environ = .empty,
176 });
174 defer threaded.deinit();177 defer threaded.deinit();
175 const io = threaded.io();178 const io = threaded.io();
176179