authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-02 22:56:52-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-03 07:49:05-05:00
logaaaaab9ec2d5d6a4c1ed2b7171bd6f599f7a31ce
tree44e5ee62a4b653e4b1bdb4621f4ec490b9b3bad3
parentbb5006d7287607e49b71d25d263ebcc3d5845c60

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.

3 files changed, 37 insertions(+), 20 deletions(-)

lib/std/child_process.zig+35-20
......@@ -19,8 +19,15 @@ const maxInt = std.math.maxInt;
1919const assert = std.debug.assert;
2020
2121pub const ChildProcess = struct {
22 pid: if (builtin.os.tag == .windows) void else i32,
23 handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
22 pub const Id = switch (builtin.os.tag) {
23 .windows => windows.HANDLE,
24 else => os.pid_t,
25 };
26
27 /// Available after calling `spawn()`. This becomes `undefined` after calling `wait()`.
28 /// On Windows this is the hProcess.
29 /// On POSIX this is the pid.
30 id: Id,
2431 thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
2532
2633 allocator: mem.Allocator,
......@@ -105,8 +112,7 @@ pub const ChildProcess = struct {
105112 return .{
106113 .allocator = allocator,
107114 .argv = argv,
108 .pid = undefined,
109 .handle = undefined,
115 .id = undefined,
110116 .thread_handle = undefined,
111117 .err_pipe = null,
112118 .term = null,
......@@ -131,6 +137,7 @@ pub const ChildProcess = struct {
131137 }
132138
133139 /// On success must call `kill` or `wait`.
140 /// After spawning the `id` is available.
134141 pub fn spawn(self: *ChildProcess) SpawnError!void {
135142 if (!std.process.can_spawn) {
136143 @compileError("the target operating system cannot spawn processes");
......@@ -167,7 +174,7 @@ pub const ChildProcess = struct {
167174 return term;
168175 }
169176
170 try windows.TerminateProcess(self.handle, exit_code);
177 try windows.TerminateProcess(self.id, exit_code);
171178 try self.waitUnwrappedWindows();
172179 return self.term.?;
173180 }
......@@ -177,18 +184,21 @@ pub const ChildProcess = struct {
177184 self.cleanupStreams();
178185 return term;
179186 }
180 try os.kill(self.pid, os.SIG.TERM);
187 try os.kill(self.id, os.SIG.TERM);
181188 try self.waitUnwrapped();
182189 return self.term.?;
183190 }
184191
185192 /// Blocks until child process terminates and then cleans up all resources.
186193 pub fn wait(self: *ChildProcess) !Term {
187 if (builtin.os.tag == .windows) {
188 return self.waitWindows();
189 } else {
190 return self.waitPosix();
191 }
194 const term = if (builtin.os.tag == .windows)
195 try self.waitWindows()
196 else
197 try self.waitPosix();
198
199 self.id = undefined;
200
201 return term;
192202 }
193203
194204 pub const ExecResult = struct {
......@@ -246,6 +256,11 @@ pub const ChildProcess = struct {
246256 stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr));
247257 }
248258
259 pub const ExecError = os.GetCwdError || os.ReadError || SpawnError || os.PollError || error{
260 StdoutStreamTooLong,
261 StderrStreamTooLong,
262 };
263
249264 /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
250265 /// If it succeeds, the caller owns result.stdout and result.stderr memory.
251266 pub fn exec(args: struct {
......@@ -256,7 +271,7 @@ pub const ChildProcess = struct {
256271 env_map: ?*const EnvMap = null,
257272 max_output_bytes: usize = 50 * 1024,
258273 expand_arg0: Arg0Expand = .no_expand,
259 }) !ExecResult {
274 }) ExecError!ExecResult {
260275 var child = ChildProcess.init(args.argv, args.allocator);
261276 child.stdin_behavior = .Ignore;
262277 child.stdout_behavior = .Pipe;
......@@ -304,18 +319,18 @@ pub const ChildProcess = struct {
304319 }
305320
306321 fn waitUnwrappedWindows(self: *ChildProcess) !void {
307 const result = windows.WaitForSingleObjectEx(self.handle, windows.INFINITE, false);
322 const result = windows.WaitForSingleObjectEx(self.id, windows.INFINITE, false);
308323
309324 self.term = @as(SpawnError!Term, x: {
310325 var exit_code: windows.DWORD = undefined;
311 if (windows.kernel32.GetExitCodeProcess(self.handle, &exit_code) == 0) {
326 if (windows.kernel32.GetExitCodeProcess(self.id, &exit_code) == 0) {
312327 break :x Term{ .Unknown = 0 };
313328 } else {
314329 break :x Term{ .Exited = @truncate(u8, exit_code) };
315330 }
316331 });
317332
318 os.close(self.handle);
333 os.close(self.id);
319334 os.close(self.thread_handle);
320335 self.cleanupStreams();
321336 return result;
......@@ -323,9 +338,9 @@ pub const ChildProcess = struct {
323338
324339 fn waitUnwrapped(self: *ChildProcess) !void {
325340 const res: os.WaitPidResult = if (comptime builtin.target.isDarwin())
326 try os.posix_spawn.waitpid(self.pid, 0)
341 try os.posix_spawn.waitpid(self.id, 0)
327342 else
328 os.waitpid(self.pid, 0);
343 os.waitpid(self.id, 0);
329344 const status = res.status;
330345 self.cleanupStreams();
331346 self.handleWaitResult(status);
......@@ -483,7 +498,7 @@ pub const ChildProcess = struct {
483498 self.stderr = null;
484499 }
485500
486 self.pid = pid;
501 self.id = pid;
487502 self.term = null;
488503
489504 if (self.stdin_behavior == StdIo.Pipe) {
......@@ -657,7 +672,7 @@ pub const ChildProcess = struct {
657672 self.stderr = null;
658673 }
659674
660 self.pid = pid;
675 self.id = pid;
661676 self.err_pipe = err_pipe;
662677 self.term = null;
663678
......@@ -923,7 +938,7 @@ pub const ChildProcess = struct {
923938 self.stderr = null;
924939 }
925940
926 self.handle = piProcInfo.hProcess;
941 self.id = piProcInfo.hProcess;
927942 self.thread_handle = piProcInfo.hThread;
928943 self.term = null;
929944
lib/std/process.zig+1
......@@ -9,6 +9,7 @@ const assert = std.debug.assert;
99const testing = std.testing;
1010const child_process = @import("child_process.zig");
1111
12pub const Child = child_process.ChildProcess;
1213pub const abort = os.abort;
1314pub const exit = os.exit;
1415pub const changeCurDir = os.chdir;
lib/std/std.zig+1
......@@ -12,6 +12,7 @@ pub const BoundedArray = @import("bounded_array.zig").BoundedArray;
1212pub const Build = @import("Build.zig");
1313pub const BufMap = @import("buf_map.zig").BufMap;
1414pub const BufSet = @import("buf_set.zig").BufSet;
15/// Deprecated: use `process.Child`.
1516pub const ChildProcess = @import("child_process.zig").ChildProcess;
1617pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap;
1718pub const DynLib = @import("dynamic_library.zig").DynLib;