Commit aaaaab9ec2

Andrew Kelley <andrew@ziglang.org>
2023-03-03 06:56:52
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.
1 parent bb5006d
Changed files (3)
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;
 
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;
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;