Command: use std.c.waitpid instead of std.posix.waitpid

This commit is contained in:
Will Pragnell
2025-01-09 20:56:16 -08:00
parent 6c71368c09
commit 5a3a3fbd45
2 changed files with 19 additions and 62 deletions

View File

@ -81,34 +81,6 @@ pub const LinuxCgroup = if (builtin.os.tag == .linux) ?[]const u8 else void;
pub const linux_cgroup_default = if (LinuxCgroup == void) pub const linux_cgroup_default = if (LinuxCgroup == void)
{} else null; {} else null;
/// The various methods a process may exit.
pub const Exit = if (builtin.os.tag == .windows) union(enum) {
Exited: u32,
} else union(enum) {
/// Exited by normal exit call, value is exit status
Exited: u8,
/// Exited by a signal, value is the signal
Signal: u32,
/// Exited by a stop signal, value is signal
Stopped: u32,
/// Unknown exit reason, value is the status from waitpid
Unknown: u32,
pub fn init(status: u32) Exit {
return if (posix.W.IFEXITED(status))
Exit{ .Exited = posix.W.EXITSTATUS(status) }
else if (posix.W.IFSIGNALED(status))
Exit{ .Signal = posix.W.TERMSIG(status) }
else if (posix.W.IFSTOPPED(status))
Exit{ .Stopped = posix.W.STOPSIG(status) }
else
Exit{ .Unknown = status };
}
};
/// Start the subprocess. This returns immediately once the child is started. /// Start the subprocess. This returns immediately once the child is started.
/// ///
/// After this is successful, self.pid is available. /// After this is successful, self.pid is available.
@ -334,7 +306,7 @@ fn setupFd(src: File.Handle, target: i32) !void {
} }
/// Wait for the command to exit and return information about how it exited. /// Wait for the command to exit and return information about how it exited.
pub fn wait(self: Command, block: bool) !Exit { pub fn wait(self: Command, block: bool) i32 {
if (comptime builtin.os.tag == .windows) { if (comptime builtin.os.tag == .windows) {
// Block until the process exits. This returns immediately if the // Block until the process exits. This returns immediately if the
// process already exited. // process already exited.
@ -349,24 +321,14 @@ pub fn wait(self: Command, block: bool) !Exit {
return windows.unexpectedError(windows.kernel32.GetLastError()); return windows.unexpectedError(windows.kernel32.GetLastError());
} }
return .{ .Exited = exit_code }; // If Windows exit codes can be larger than i32-MAX, this is potentially
// unsafe, but that seems very unlikely
return @intCast(exit_code);
} }
const res = if (block) posix.waitpid(self.pid.?, 0) else res: { var options: c_int = 0;
// We specify NOHANG because its not our fault if the process we launch if (!block) options = std.c.W.NOHANG;
// for the tty doesn't properly waitpid its children. We don't want return std.c.waitpid(self.pid.?, null, options);
// to hang the terminal over it.
// When NOHANG is specified, waitpid will return a pid of 0 if the process
// doesn't have a status to report. When that happens, it is as though the
// wait call has not been performed, so we need to keep trying until we get
// a non-zero pid back, otherwise we end up with zombie processes.
while (true) {
const res = posix.waitpid(self.pid.?, std.c.W.NOHANG);
if (res.pid != 0) break :res res;
}
};
return Exit.init(res.status);
} }
/// Sets command->data to data. /// Sets command->data to data.
@ -605,9 +567,8 @@ test "Command: pre exec" {
try cmd.testingStart(); try cmd.testingStart();
try testing.expect(cmd.pid != null); try testing.expect(cmd.pid != null);
const exit = try cmd.wait(true); const pid = cmd.wait(true);
try testing.expect(exit == .Exited); try testing.expectEqual(cmd.pid, pid);
try testing.expect(exit.Exited == 42);
} }
fn createTestStdout(dir: std.fs.Dir) !File { fn createTestStdout(dir: std.fs.Dir) !File {
@ -641,9 +602,8 @@ test "Command: redirect stdout to file" {
try cmd.testingStart(); try cmd.testingStart();
try testing.expect(cmd.pid != null); try testing.expect(cmd.pid != null);
const exit = try cmd.wait(true); const pid = cmd.wait(true);
try testing.expect(exit == .Exited); try testing.expectEqual(cmd.pid, pid);
try testing.expectEqual(@as(u32, 0), @as(u32, exit.Exited));
// Read our stdout // Read our stdout
try stdout.seekTo(0); try stdout.seekTo(0);
@ -676,9 +636,8 @@ test "Command: custom env vars" {
try cmd.testingStart(); try cmd.testingStart();
try testing.expect(cmd.pid != null); try testing.expect(cmd.pid != null);
const exit = try cmd.wait(true); const pid = cmd.wait(true);
try testing.expect(exit == .Exited); try testing.expectEqual(cmd.pid, pid);
try testing.expect(exit.Exited == 0);
// Read our stdout // Read our stdout
try stdout.seekTo(0); try stdout.seekTo(0);
@ -712,9 +671,8 @@ test "Command: custom working directory" {
try cmd.testingStart(); try cmd.testingStart();
try testing.expect(cmd.pid != null); try testing.expect(cmd.pid != null);
const exit = try cmd.wait(true); const pid = cmd.wait(true);
try testing.expect(exit == .Exited); try testing.expectEqual(cmd.pid, pid);
try testing.expect(exit.Exited == 0);
// Read our stdout // Read our stdout
try stdout.seekTo(0); try stdout.seekTo(0);
@ -754,9 +712,8 @@ test "Command: posix fork handles execveZ failure" {
try cmd.testingStart(); try cmd.testingStart();
try testing.expect(cmd.pid != null); try testing.expect(cmd.pid != null);
const exit = try cmd.wait(true); const pid = cmd.wait(true);
try testing.expect(exit == .Exited); try testing.expectEqual(cmd.pid, pid);
try testing.expect(exit.Exited == 1);
} }
// If cmd.start fails with error.ExecFailedInChild it's the _child_ process that is running. If it does not // If cmd.start fails with error.ExecFailedInChild it's the _child_ process that is running. If it does not

View File

@ -1204,8 +1204,8 @@ const Subprocess = struct {
/// process and unset our running state. /// process and unset our running state.
pub fn externalExit(self: *Subprocess) void { pub fn externalExit(self: *Subprocess) void {
if (self.command) |*cmd| { if (self.command) |*cmd| {
_ = cmd.wait(false) catch |err| const ret = cmd.wait(false);
log.err("error waiting on subprocess: {}", .{err}); log.debug("wait result={}", .{ret});
} }
self.command = null; self.command = null;
} }