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;...@@ -19,8 +19,15 @@ const maxInt = std.math.maxInt;
19const assert = std.debug.assert;19const assert = std.debug.assert;
2020
21pub const ChildProcess = struct {21pub const ChildProcess = struct {
22 pid: if (builtin.os.tag == .windows) void else i32,22 pub const Id = switch (builtin.os.tag) {
23 handle: if (builtin.os.tag == .windows) windows.HANDLE else void,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,
24 thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void,31 thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
2532
26 allocator: mem.Allocator,33 allocator: mem.Allocator,
...@@ -105,8 +112,7 @@ pub const ChildProcess = struct {...@@ -105,8 +112,7 @@ pub const ChildProcess = struct {
105 return .{112 return .{
106 .allocator = allocator,113 .allocator = allocator,
107 .argv = argv,114 .argv = argv,
108 .pid = undefined,115 .id = undefined,
109 .handle = undefined,
110 .thread_handle = undefined,116 .thread_handle = undefined,
111 .err_pipe = null,117 .err_pipe = null,
112 .term = null,118 .term = null,
...@@ -131,6 +137,7 @@ pub const ChildProcess = struct {...@@ -131,6 +137,7 @@ pub const ChildProcess = struct {
131 }137 }
132138
133 /// On success must call `kill` or `wait`.139 /// On success must call `kill` or `wait`.
140 /// After spawning the `id` is available.
134 pub fn spawn(self: *ChildProcess) SpawnError!void {141 pub fn spawn(self: *ChildProcess) SpawnError!void {
135 if (!std.process.can_spawn) {142 if (!std.process.can_spawn) {
136 @compileError("the target operating system cannot spawn processes");143 @compileError("the target operating system cannot spawn processes");
...@@ -167,7 +174,7 @@ pub const ChildProcess = struct {...@@ -167,7 +174,7 @@ pub const ChildProcess = struct {
167 return term;174 return term;
168 }175 }
169176
170 try windows.TerminateProcess(self.handle, exit_code);177 try windows.TerminateProcess(self.id, exit_code);
171 try self.waitUnwrappedWindows();178 try self.waitUnwrappedWindows();
172 return self.term.?;179 return self.term.?;
173 }180 }
...@@ -177,18 +184,21 @@ pub const ChildProcess = struct {...@@ -177,18 +184,21 @@ pub const ChildProcess = struct {
177 self.cleanupStreams();184 self.cleanupStreams();
178 return term;185 return term;
179 }186 }
180 try os.kill(self.pid, os.SIG.TERM);187 try os.kill(self.id, os.SIG.TERM);
181 try self.waitUnwrapped();188 try self.waitUnwrapped();
182 return self.term.?;189 return self.term.?;
183 }190 }
184191
185 /// Blocks until child process terminates and then cleans up all resources.192 /// Blocks until child process terminates and then cleans up all resources.
186 pub fn wait(self: *ChildProcess) !Term {193 pub fn wait(self: *ChildProcess) !Term {
187 if (builtin.os.tag == .windows) {194 const term = if (builtin.os.tag == .windows)
188 return self.waitWindows();195 try self.waitWindows()
189 } else {196 else
190 return self.waitPosix();197 try self.waitPosix();
191 }198
199 self.id = undefined;
200
201 return term;
192 }202 }
193203
194 pub const ExecResult = struct {204 pub const ExecResult = struct {
...@@ -246,6 +256,11 @@ pub const ChildProcess = struct {...@@ -246,6 +256,11 @@ pub const ChildProcess = struct {
246 stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr));256 stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr));
247 }257 }
248258
259 pub const ExecError = os.GetCwdError || os.ReadError || SpawnError || os.PollError || error{
260 StdoutStreamTooLong,
261 StderrStreamTooLong,
262 };
263
249 /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.264 /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
250 /// If it succeeds, the caller owns result.stdout and result.stderr memory.265 /// If it succeeds, the caller owns result.stdout and result.stderr memory.
251 pub fn exec(args: struct {266 pub fn exec(args: struct {
...@@ -256,7 +271,7 @@ pub const ChildProcess = struct {...@@ -256,7 +271,7 @@ pub const ChildProcess = struct {
256 env_map: ?*const EnvMap = null,271 env_map: ?*const EnvMap = null,
257 max_output_bytes: usize = 50 * 1024,272 max_output_bytes: usize = 50 * 1024,
258 expand_arg0: Arg0Expand = .no_expand,273 expand_arg0: Arg0Expand = .no_expand,
259 }) !ExecResult {274 }) ExecError!ExecResult {
260 var child = ChildProcess.init(args.argv, args.allocator);275 var child = ChildProcess.init(args.argv, args.allocator);
261 child.stdin_behavior = .Ignore;276 child.stdin_behavior = .Ignore;
262 child.stdout_behavior = .Pipe;277 child.stdout_behavior = .Pipe;
...@@ -304,18 +319,18 @@ pub const ChildProcess = struct {...@@ -304,18 +319,18 @@ pub const ChildProcess = struct {
304 }319 }
305320
306 fn waitUnwrappedWindows(self: *ChildProcess) !void {321 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
309 self.term = @as(SpawnError!Term, x: {324 self.term = @as(SpawnError!Term, x: {
310 var exit_code: windows.DWORD = undefined;325 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) {
312 break :x Term{ .Unknown = 0 };327 break :x Term{ .Unknown = 0 };
313 } else {328 } else {
314 break :x Term{ .Exited = @truncate(u8, exit_code) };329 break :x Term{ .Exited = @truncate(u8, exit_code) };
315 }330 }
316 });331 });
317332
318 os.close(self.handle);333 os.close(self.id);
319 os.close(self.thread_handle);334 os.close(self.thread_handle);
320 self.cleanupStreams();335 self.cleanupStreams();
321 return result;336 return result;
...@@ -323,9 +338,9 @@ pub const ChildProcess = struct {...@@ -323,9 +338,9 @@ pub const ChildProcess = struct {
323338
324 fn waitUnwrapped(self: *ChildProcess) !void {339 fn waitUnwrapped(self: *ChildProcess) !void {
325 const res: os.WaitPidResult = if (comptime builtin.target.isDarwin())340 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)
327 else342 else
328 os.waitpid(self.pid, 0);343 os.waitpid(self.id, 0);
329 const status = res.status;344 const status = res.status;
330 self.cleanupStreams();345 self.cleanupStreams();
331 self.handleWaitResult(status);346 self.handleWaitResult(status);
...@@ -483,7 +498,7 @@ pub const ChildProcess = struct {...@@ -483,7 +498,7 @@ pub const ChildProcess = struct {
483 self.stderr = null;498 self.stderr = null;
484 }499 }
485500
486 self.pid = pid;501 self.id = pid;
487 self.term = null;502 self.term = null;
488503
489 if (self.stdin_behavior == StdIo.Pipe) {504 if (self.stdin_behavior == StdIo.Pipe) {
...@@ -657,7 +672,7 @@ pub const ChildProcess = struct {...@@ -657,7 +672,7 @@ pub const ChildProcess = struct {
657 self.stderr = null;672 self.stderr = null;
658 }673 }
659674
660 self.pid = pid;675 self.id = pid;
661 self.err_pipe = err_pipe;676 self.err_pipe = err_pipe;
662 self.term = null;677 self.term = null;
663678
...@@ -923,7 +938,7 @@ pub const ChildProcess = struct {...@@ -923,7 +938,7 @@ pub const ChildProcess = struct {
923 self.stderr = null;938 self.stderr = null;
924 }939 }
925940
926 self.handle = piProcInfo.hProcess;941 self.id = piProcInfo.hProcess;
927 self.thread_handle = piProcInfo.hThread;942 self.thread_handle = piProcInfo.hThread;
928 self.term = null;943 self.term = null;
929944
lib/std/process.zig+1
...@@ -9,6 +9,7 @@ const assert = std.debug.assert;...@@ -9,6 +9,7 @@ const assert = std.debug.assert;
9const testing = std.testing;9const testing = std.testing;
10const child_process = @import("child_process.zig");10const child_process = @import("child_process.zig");
1111
12pub const Child = child_process.ChildProcess;
12pub const abort = os.abort;13pub const abort = os.abort;
13pub const exit = os.exit;14pub const exit = os.exit;
14pub const changeCurDir = os.chdir;15pub const changeCurDir = os.chdir;
lib/std/std.zig+1
...@@ -12,6 +12,7 @@ pub const BoundedArray = @import("bounded_array.zig").BoundedArray;...@@ -12,6 +12,7 @@ pub const BoundedArray = @import("bounded_array.zig").BoundedArray;
12pub const Build = @import("Build.zig");12pub const Build = @import("Build.zig");
13pub const BufMap = @import("buf_map.zig").BufMap;13pub const BufMap = @import("buf_map.zig").BufMap;
14pub const BufSet = @import("buf_set.zig").BufSet;14pub const BufSet = @import("buf_set.zig").BufSet;
15/// Deprecated: use `process.Child`.
15pub const ChildProcess = @import("child_process.zig").ChildProcess;16pub const ChildProcess = @import("child_process.zig").ChildProcess;
16pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap;17pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap;
17pub const DynLib = @import("dynamic_library.zig").DynLib;18pub const DynLib = @import("dynamic_library.zig").DynLib;