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(
11881188 .argv0 = options.argv0,
11891189 .worker_threads = .init(null),
11901190 .environ = .{ .process_environ = options.environ },
1191 .robust_cancel = options.robust_cancel,
11921191 };
11931192
11941193 if (posix.Sigaction != void) {
......@@ -13050,9 +13049,10 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce
1305013049fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitError!process.Child.Term {
1305113050 if (native_os == .wasi) unreachable;
1305213051 const t: *Threaded = @ptrCast(@alignCast(userdata));
13052 _ = t;
1305313053 switch (native_os) {
13054 .windows => return childWaitWindows(t, child),
13055 else => return childWaitPosix(Thread.getCurrent(t), child),
13054 .windows => return childWaitWindows(child),
13055 else => return childWaitPosix(child),
1305613056 }
1305713057}
1305813058
......@@ -13062,7 +13062,8 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void {
1306213062 if (is_windows) {
1306313063 childKillWindows(t, child, 1) catch childCleanupWindows(child);
1306413064 } else {
13065 childKillPosix(Thread.getCurrent(t), child) catch childCleanupPosix(child);
13065 childKillPosix(child) catch {};
13066 childCleanupPosix(child);
1306613067 }
1306713068}
1306813069
......@@ -13087,21 +13088,24 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT
1308713088 childCleanupWindows(child);
1308813089}
1308913090
13090fn childWaitWindows(t: *Threaded, child: *process.Child) process.Child.WaitError!process.Child.Term {
13091 const current_thread = Thread.getCurrent(t);
13091fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term {
1309213092 const handle = child.id.?;
1309313093
13094 while (true) {
13095 try current_thread.checkCancel();
13096 switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) {
13097 windows.WAIT_OBJECT_0 => break,
13098 windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => continue,
13099 windows.WAIT_FAILED => switch (windows.GetLastError()) {
13094 var syscall: Syscall = try .start();
13095 while (true) switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) {
13096 windows.WAIT_OBJECT_0 => break syscall.finish(),
13097 windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => {
13098 try syscall.checkCancel();
13099 continue;
13100 },
13101 windows.WAIT_FAILED => {
13102 syscall.finish();
13103 switch (windows.GetLastError()) {
1310013104 else => |err| return windows.unexpectedError(err),
13101 },
13102 else => return error.Unexpected,
13103 }
13104 }
13105 }
13106 },
13107 else => return syscall.fail(error.Unexpected),
13108 };
1310513109
1310613110 const term: process.Child.Term = x: {
1310713111 var exit_code: windows.DWORD = undefined;
......@@ -13142,7 +13146,7 @@ fn childCleanupWindows(child: *process.Child) void {
1314213146 }
1314313147}
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 {
1314613150 defer childCleanupPosix(child);
1314713151
1314813152 const pid = child.id.?;
......@@ -13152,29 +13156,29 @@ fn childWaitPosix(current_thread: *Thread, child: *process.Child) process.Child.
1315213156
1315313157 if (have_wait4) {
1315413158 var status: if (builtin.link_libc) c_int else u32 = undefined;
13155 try current_thread.beginSyscall();
13159 const syscall: Syscall = try .start();
1315613160 while (true) switch (posix.errno(posix.system.wait4(pid, &status, 0, ru_ptr))) {
1315713161 .SUCCESS => {
13158 current_thread.endSyscall();
13162 syscall.finish();
1315913163 if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*;
1316013164 return statusToTerm(@bitCast(status));
1316113165 },
1316213166 .INTR => {
13163 try current_thread.checkCancel();
13167 try syscall.checkCancel();
1316413168 continue;
1316513169 },
13166 .CHILD => |err| return current_thread.endSyscallErrnoBug(err), // Double-free.
13167 else => |err| return current_thread.endSyscallUnexpectedErrno(err),
13170 .CHILD => |err| return syscall.errnoBug(err), // Double-free.
13171 else => |err| return syscall.unexpectedErrno(err),
1316813172 };
1316913173 }
1317013174
1317113175 if (have_waitid) {
1317213176 const linux = std.os.linux; // Bypass libc which has the wrong signature.
1317313177 var info: linux.siginfo_t = undefined;
13174 try current_thread.beginSyscall();
13178 const syscall: Syscall = try .start();
1317513179 while (true) switch (linux.errno(linux.waitid(.PID, pid, &info, linux.W.EXITED, ru_ptr))) {
1317613180 .SUCCESS => {
13177 current_thread.endSyscall();
13181 syscall.finish();
1317813182 if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*;
1317913183 const status: u32 = @bitCast(info.fields.common.second.sigchld.status);
1318013184 const code: linux.CLD = @enumFromInt(info.code);
......@@ -13186,26 +13190,27 @@ fn childWaitPosix(current_thread: *Thread, child: *process.Child) process.Child.
1318613190 };
1318713191 },
1318813192 .INTR => {
13189 try current_thread.checkCancel();
13193 try syscall.checkCancel();
1319013194 continue;
1319113195 },
13192 .CHILD => |err| return current_thread.endSyscallErrnoBug(err), // Double-free.
13193 else => |err| return current_thread.endSyscallUnexpectedErrno(err),
13196 .CHILD => |err| return syscall.errnoBug(err), // Double-free.
13197 else => |err| return syscall.unexpectedErrno(err),
1319413198 };
1319513199 }
1319613200
1319713201 var status: if (builtin.link_libc) c_int else u32 = undefined;
13202 const syscall: Syscall = try .start();
1319813203 while (true) switch (posix.errno(posix.system.waitpid(pid, &status, 0))) {
1319913204 .SUCCESS => {
13200 current_thread.endSyscall();
13205 syscall.finish();
1320113206 return statusToTerm(@bitCast(status));
1320213207 },
1320313208 .INTR => {
13204 try current_thread.checkCancel();
13209 try syscall.checkCancel();
1320513210 continue;
1320613211 },
13207 .CHILD => |err| return current_thread.endSyscallErrnoBug(err), // Double-free.
13208 else => |err| return current_thread.endSyscallUnexpectedErrno(err),
13212 .CHILD => |err| return syscall.errnoBug(err), // Double-free.
13213 else => |err| return syscall.unexpectedErrno(err),
1320913214 };
1321013215}
1321113216
......@@ -13220,9 +13225,12 @@ fn statusToTerm(status: u32) process.Child.Term {
1322013225 .{ .unknown = status };
1322113226}
1322213227
13223fn childKillPosix(current_thread: *Thread, child: *process.Child) !void {
13224 // Intentionally uncancelable.
13225 while (true) switch (posix.errno(posix.system.kill(child.id.?, .TERM))) {
13228fn childKillPosix(child: *process.Child) !void {
13229 // Entire function body is intentionally uncancelable.
13230
13231 const pid = child.id.?;
13232
13233 while (true) switch (posix.errno(posix.system.kill(pid, .TERM))) {
1322613234 .SUCCESS => break,
1322713235 .INTR => continue,
1322813236 .PERM => return error.PermissionDenied,
......@@ -13230,7 +13238,35 @@ fn childKillPosix(current_thread: *Thread, child: *process.Child) !void {
1323013238 .SRCH => |err| return errnoBug(err),
1323113239 else => |err| return posix.unexpectedErrno(err),
1323213240 };
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 };
1323413270}
1323513271
1323613272fn childCleanupPosix(child: *process.Child) void {
......@@ -13537,7 +13573,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
1353713573 }
1353813574
1353913575 windowsCreateProcessPathExt(
13540 t,
1354113576 arena,
1354213577 &dir_buf,
1354313578 &app_buf,
......@@ -13573,7 +13608,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
1357313608 try dir_buf.appendSlice(arena, search_path);
1357413609
1357513610 if (windowsCreateProcessPathExt(
13576 t,
1357713611 arena,
1357813612 &dir_buf,
1357913613 &app_buf,
lib/std/Io/Threaded/test.zig+4-1
......@@ -170,7 +170,10 @@ test "cancel blocked read from pipe" {
170170 }
171171 };
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 });
174177 defer threaded.deinit();
175178 const io = threaded.io();
176179