authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-02 20:19:32-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-04 00:27:08-08:00
logc6b75b61b7dc914c46de86aa764958f6954d28a4
treebb11834317287dc2d9ae28ae0f25decdd1a6458c
parentd97e4ca0d1de34c8987e675b4d61312e42a1d984

std: fix child processes on riscv32-linux


3 files changed, 94 insertions(+), 60 deletions(-)

lib/std/Io/Threaded.zig+75-17
......@@ -1638,8 +1638,14 @@ const have_fchmod = switch (native_os) {
16381638 else => true,
16391639};
16401640
1641const have_waitid = switch (native_os) {
1642 .linux => @hasField(std.os.linux.SYS, "waitid"),
1643 else => false,
1644};
1645
16411646const have_wait4 = switch (native_os) {
1642 .dragonfly, .freebsd, .netbsd, .openbsd, .illumos, .linux, .serenity, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => true,
1647 .linux => @hasField(std.os.linux.SYS, "wait4"),
1648 .dragonfly, .freebsd, .netbsd, .openbsd, .illumos, .serenity, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => true,
16431649 else => false,
16441650};
16451651
......@@ -13019,7 +13025,7 @@ fn childWait(userdata: ?*anyopaque, child: *std.process.Child) process.Child.Wai
1301913025 const t: *Threaded = @ptrCast(@alignCast(userdata));
1302013026 switch (native_os) {
1302113027 .windows => return childWaitWindows(t, child),
13022 else => return childWaitPosix(t, child),
13028 else => return childWaitPosix(Thread.getCurrent(t), child),
1302313029 }
1302413030}
1302513031
......@@ -13029,7 +13035,7 @@ fn childKill(userdata: ?*anyopaque, child: *std.process.Child) void {
1302913035 if (is_windows) {
1303013036 childKillWindows(t, child, 1) catch childCleanupWindows(child);
1303113037 } else {
13032 childKillPosix(t, child) catch childCleanupPosix(child);
13038 childKillPosix(Thread.getCurrent(t), child) catch childCleanupPosix(child);
1303313039 }
1303413040}
1303513041
......@@ -13109,20 +13115,71 @@ fn childCleanupWindows(child: *process.Child) void {
1310913115 }
1311013116}
1311113117
13112fn childWaitPosix(t: *Threaded, child: *process.Child) process.Child.WaitError!process.Child.Term {
13113 _ = t; // TODO cancelation
13118fn childWaitPosix(current_thread: *Thread, child: *process.Child) process.Child.WaitError!process.Child.Term {
13119 defer childCleanupPosix(child);
13120
1311413121 const pid = child.id.?;
13115 const res: posix.WaitPidResult = res: {
13116 if (child.request_resource_usage_statistics and have_wait4) {
13117 var ru: posix.rusage = undefined;
13118 const res = posix.wait4(pid, 0, &ru);
13119 child.resource_usage_statistics.rusage = ru;
13120 break :res res;
13121 }
13122 break :res posix.waitpid(pid, 0);
13122
13123 var ru: posix.rusage = undefined;
13124 const ru_ptr = if (child.request_resource_usage_statistics) &ru else null;
13125
13126 if (have_wait4) {
13127 var status: if (builtin.link_libc) c_int else u32 = undefined;
13128 try current_thread.beginSyscall();
13129 while (true) switch (posix.errno(posix.system.wait4(pid, &status, 0, ru_ptr))) {
13130 .SUCCESS => {
13131 current_thread.endSyscall();
13132 if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*;
13133 return statusToTerm(@bitCast(status));
13134 },
13135 .INTR => {
13136 try current_thread.checkCancel();
13137 continue;
13138 },
13139 .CHILD => |err| return current_thread.endSyscallErrnoBug(err), // Double-free.
13140 else => |err| return current_thread.endSyscallUnexpectedErrno(err),
13141 };
13142 }
13143
13144 if (have_waitid) {
13145 const linux = std.os.linux; // Bypass libc which has the wrong signature.
13146 var info: linux.siginfo_t = undefined;
13147 try current_thread.beginSyscall();
13148 while (true) switch (linux.errno(linux.waitid(.PID, pid, &info, linux.W.EXITED, ru_ptr))) {
13149 .SUCCESS => {
13150 current_thread.endSyscall();
13151 if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*;
13152 const status: u32 = @bitCast(info.fields.common.second.sigchld.status);
13153 const code: linux.CLD = @enumFromInt(info.code);
13154 return switch (code) {
13155 .EXITED => .{ .exited = @truncate(status) },
13156 .KILLED, .DUMPED => .{ .signal = @enumFromInt(status) },
13157 .TRAPPED, .STOPPED => .{ .stopped = status },
13158 _, .CONTINUED => .{ .unknown = status },
13159 };
13160 },
13161 .INTR => {
13162 try current_thread.checkCancel();
13163 continue;
13164 },
13165 .CHILD => |err| return current_thread.endSyscallErrnoBug(err), // Double-free.
13166 else => |err| return current_thread.endSyscallUnexpectedErrno(err),
13167 };
13168 }
13169
13170 var status: if (builtin.link_libc) c_int else u32 = undefined;
13171 while (true) switch (posix.errno(posix.system.waitpid(pid, &status, 0))) {
13172 .SUCCESS => {
13173 current_thread.endSyscall();
13174 return statusToTerm(@bitCast(status));
13175 },
13176 .INTR => {
13177 try current_thread.checkCancel();
13178 continue;
13179 },
13180 .CHILD => |err| return current_thread.endSyscallErrnoBug(err), // Double-free.
13181 else => |err| return current_thread.endSyscallUnexpectedErrno(err),
1312313182 };
13124 childCleanupPosix(child);
13125 return statusToTerm(res.status);
1312613183}
1312713184
1312813185fn statusToTerm(status: u32) process.Child.Term {
......@@ -13136,7 +13193,8 @@ fn statusToTerm(status: u32) process.Child.Term {
1313613193 .{ .unknown = status };
1313713194}
1313813195
13139fn childKillPosix(t: *Threaded, child: *process.Child) !void {
13196fn childKillPosix(current_thread: *Thread, child: *process.Child) !void {
13197 // Intentionally uncancelable.
1314013198 while (true) switch (posix.errno(posix.system.kill(child.id.?, .TERM))) {
1314113199 .SUCCESS => break,
1314213200 .INTR => continue,
......@@ -13145,7 +13203,7 @@ fn childKillPosix(t: *Threaded, child: *process.Child) !void {
1314513203 .SRCH => |err| return errnoBug(err),
1314613204 else => |err| return posix.unexpectedErrno(err),
1314713205 };
13148 _ = try childWaitPosix(t, child);
13206 _ = try childWaitPosix(current_thread, child);
1314913207}
1315013208
1315113209fn childCleanupPosix(child: *process.Child) void {
lib/std/os/linux.zig+19-2
......@@ -1598,8 +1598,15 @@ pub fn wait4(pid: pid_t, status: *u32, flags: u32, usage: ?*rusage) usize {
15981598 );
15991599}
16001600
1601pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize {
1602 return syscall5(.waitid, @intFromEnum(id_type), @as(usize, @bitCast(@as(isize, id))), @intFromPtr(infop), flags, 0);
1601pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32, usage: ?*rusage) usize {
1602 return syscall5(
1603 .waitid,
1604 @intFromEnum(id_type),
1605 @as(usize, @bitCast(@as(isize, id))),
1606 @intFromPtr(infop),
1607 flags,
1608 @intFromPtr(usage),
1609 );
16031610}
16041611
16051612pub const F = struct {
......@@ -6205,6 +6212,16 @@ const siginfo_fields_union = extern union {
62056212 },
62066213};
62076214
6215pub const CLD = enum(i32) {
6216 EXITED = 1,
6217 KILLED = 2,
6218 DUMPED = 3,
6219 TRAPPED = 4,
6220 STOPPED = 5,
6221 CONTINUED = 6,
6222 _,
6223};
6224
62086225pub const siginfo_t = if (is_mips)
62096226 extern struct {
62106227 signo: SIG,
lib/std/posix.zig-41
......@@ -1690,47 +1690,6 @@ pub fn getsockoptError(sockfd: fd_t) ConnectError!void {
16901690 }
16911691}
16921692
1693pub const WaitPidResult = struct {
1694 pid: pid_t,
1695 status: u32,
1696};
1697
1698/// Use this version of the `waitpid` wrapper if you spawned your child process using explicit
1699/// `fork` and `execve` method.
1700pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {
1701 var status: if (builtin.link_libc) c_int else u32 = undefined;
1702 while (true) {
1703 const rc = system.waitpid(pid, &status, @intCast(flags));
1704 switch (errno(rc)) {
1705 .SUCCESS => return .{
1706 .pid = @intCast(rc),
1707 .status = @bitCast(status),
1708 },
1709 .INTR => continue,
1710 .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
1711 .INVAL => unreachable, // Invalid flags.
1712 else => unreachable,
1713 }
1714 }
1715}
1716
1717pub fn wait4(pid: pid_t, flags: u32, ru: ?*rusage) WaitPidResult {
1718 var status: if (builtin.link_libc) c_int else u32 = undefined;
1719 while (true) {
1720 const rc = system.wait4(pid, &status, @intCast(flags), ru);
1721 switch (errno(rc)) {
1722 .SUCCESS => return .{
1723 .pid = @intCast(rc),
1724 .status = @bitCast(status),
1725 },
1726 .INTR => continue,
1727 .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
1728 .INVAL => unreachable, // Invalid flags.
1729 else => unreachable,
1730 }
1731 }
1732}
1733
17341693pub const FStatError = std.Io.File.StatError;
17351694
17361695/// Return information about a file descriptor.