From aaaaab9ec2d5d6a4c1ed2b7171bd6f599f7a31ce Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Mar 2023 22:56:52 -0700 Subject: [PATCH] std.process.Child: remove pid and handle, add id Previously, this API had pid, to be used on POSIX systems, and handle, to be used on Windows. This commit unifies the API, defining an Id type that is either the pid or the HANDLE depending on the target OS. This commit also prepares for the future by allowing one to import via `std.process.Child` which is the fully qualified namespace that I intend to migrate to in the future. --- lib/std/child_process.zig | 55 +++++++++++++++++++++++++-------------- lib/std/process.zig | 1 + lib/std/std.zig | 1 + 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 7ff48a5652..dba92ab998 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -19,8 +19,15 @@ const maxInt = std.math.maxInt; const assert = std.debug.assert; pub const ChildProcess = struct { - pid: if (builtin.os.tag == .windows) void else i32, - handle: if (builtin.os.tag == .windows) windows.HANDLE else void, + pub const Id = switch (builtin.os.tag) { + .windows => windows.HANDLE, + else => os.pid_t, + }; + + /// Available after calling `spawn()`. This becomes `undefined` after calling `wait()`. + /// On Windows this is the hProcess. + /// On POSIX this is the pid. + id: Id, thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void, allocator: mem.Allocator, @@ -105,8 +112,7 @@ pub const ChildProcess = struct { return .{ .allocator = allocator, .argv = argv, - .pid = undefined, - .handle = undefined, + .id = undefined, .thread_handle = undefined, .err_pipe = null, .term = null, @@ -131,6 +137,7 @@ pub const ChildProcess = struct { } /// On success must call `kill` or `wait`. + /// After spawning the `id` is available. pub fn spawn(self: *ChildProcess) SpawnError!void { if (!std.process.can_spawn) { @compileError("the target operating system cannot spawn processes"); @@ -167,7 +174,7 @@ pub const ChildProcess = struct { return term; } - try windows.TerminateProcess(self.handle, exit_code); + try windows.TerminateProcess(self.id, exit_code); try self.waitUnwrappedWindows(); return self.term.?; } @@ -177,18 +184,21 @@ pub const ChildProcess = struct { self.cleanupStreams(); return term; } - try os.kill(self.pid, os.SIG.TERM); + try os.kill(self.id, os.SIG.TERM); try self.waitUnwrapped(); return self.term.?; } /// Blocks until child process terminates and then cleans up all resources. pub fn wait(self: *ChildProcess) !Term { - if (builtin.os.tag == .windows) { - return self.waitWindows(); - } else { - return self.waitPosix(); - } + const term = if (builtin.os.tag == .windows) + try self.waitWindows() + else + try self.waitPosix(); + + self.id = undefined; + + return term; } pub const ExecResult = struct { @@ -246,6 +256,11 @@ pub const ChildProcess = struct { stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr)); } + pub const ExecError = os.GetCwdError || os.ReadError || SpawnError || os.PollError || error{ + StdoutStreamTooLong, + StderrStreamTooLong, + }; + /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. /// If it succeeds, the caller owns result.stdout and result.stderr memory. pub fn exec(args: struct { @@ -256,7 +271,7 @@ pub const ChildProcess = struct { env_map: ?*const EnvMap = null, max_output_bytes: usize = 50 * 1024, expand_arg0: Arg0Expand = .no_expand, - }) !ExecResult { + }) ExecError!ExecResult { var child = ChildProcess.init(args.argv, args.allocator); child.stdin_behavior = .Ignore; child.stdout_behavior = .Pipe; @@ -304,18 +319,18 @@ pub const ChildProcess = struct { } fn waitUnwrappedWindows(self: *ChildProcess) !void { - const result = windows.WaitForSingleObjectEx(self.handle, windows.INFINITE, false); + const result = windows.WaitForSingleObjectEx(self.id, windows.INFINITE, false); self.term = @as(SpawnError!Term, x: { var exit_code: windows.DWORD = undefined; - if (windows.kernel32.GetExitCodeProcess(self.handle, &exit_code) == 0) { + if (windows.kernel32.GetExitCodeProcess(self.id, &exit_code) == 0) { break :x Term{ .Unknown = 0 }; } else { break :x Term{ .Exited = @truncate(u8, exit_code) }; } }); - os.close(self.handle); + os.close(self.id); os.close(self.thread_handle); self.cleanupStreams(); return result; @@ -323,9 +338,9 @@ pub const ChildProcess = struct { fn waitUnwrapped(self: *ChildProcess) !void { const res: os.WaitPidResult = if (comptime builtin.target.isDarwin()) - try os.posix_spawn.waitpid(self.pid, 0) + try os.posix_spawn.waitpid(self.id, 0) else - os.waitpid(self.pid, 0); + os.waitpid(self.id, 0); const status = res.status; self.cleanupStreams(); self.handleWaitResult(status); @@ -483,7 +498,7 @@ pub const ChildProcess = struct { self.stderr = null; } - self.pid = pid; + self.id = pid; self.term = null; if (self.stdin_behavior == StdIo.Pipe) { @@ -657,7 +672,7 @@ pub const ChildProcess = struct { self.stderr = null; } - self.pid = pid; + self.id = pid; self.err_pipe = err_pipe; self.term = null; @@ -923,7 +938,7 @@ pub const ChildProcess = struct { self.stderr = null; } - self.handle = piProcInfo.hProcess; + self.id = piProcInfo.hProcess; self.thread_handle = piProcInfo.hThread; self.term = null; diff --git a/lib/std/process.zig b/lib/std/process.zig index 777bcbbab0..eff29e86fa 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -9,6 +9,7 @@ const assert = std.debug.assert; const testing = std.testing; const child_process = @import("child_process.zig"); +pub const Child = child_process.ChildProcess; pub const abort = os.abort; pub const exit = os.exit; pub const changeCurDir = os.chdir; diff --git a/lib/std/std.zig b/lib/std/std.zig index 4a6d330003..c1c682e224 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -12,6 +12,7 @@ pub const BoundedArray = @import("bounded_array.zig").BoundedArray; pub const Build = @import("Build.zig"); pub const BufMap = @import("buf_map.zig").BufMap; pub const BufSet = @import("buf_set.zig").BufSet; +/// Deprecated: use `process.Child`. pub const ChildProcess = @import("child_process.zig").ChildProcess; pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap; pub const DynLib = @import("dynamic_library.zig").DynLib;