authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-29 11:16:28-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-29 11:16:28-07:00
loge54fd2578195ce7857d921ac22b800d376fb870b
treebcfdeec3c406aeb2a0427d816d0f3d242354fdce
parent1590ed9d6aea95e5a21e3455e8edba4cdb374f2c
parent717cf00fe0d68dc1213fb645b184afe1cbf52104

Merge branch 'LemonBoy-cprocess'

This is a partial merge of #6750. I took the Posix code paths and dropped the Windows code paths, and then did the improvements noted in the comments. The Windows implementation is still TODO.

5 files changed, 123 insertions(+), 14 deletions(-)

lib/std/array_list.zig+13-3
......@@ -337,11 +337,21 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
337337 return self.pop();
338338 }
339339
340 // For a nicer API, `items.len` is the length, not the capacity.
341 // This requires "unsafe" slicing.
342 fn allocatedSlice(self: Self) Slice {
340 /// Returns a slice of all the items plus the extra capacity, whose memory
341 /// contents are undefined.
342 pub fn allocatedSlice(self: Self) Slice {
343 // For a nicer API, `items.len` is the length, not the capacity.
344 // This requires "unsafe" slicing.
343345 return self.items.ptr[0..self.capacity];
344346 }
347
348 /// Returns a slice of only the extra capacity after items.
349 /// This can be useful for writing directly into an `ArrayList`.
350 /// Note that such an operation must be followed up with a direct
351 /// modification of `self.items.len`.
352 pub fn unusedCapacitySlice(self: Self) Slice {
353 return self.allocatedSlice()[self.items.len..];
354 }
345355 };
346356}
347357
lib/std/child_process.zig+73-9
......@@ -186,6 +186,56 @@ pub const ChildProcess = struct {
186186
187187 pub const exec2 = @compileError("deprecated: exec2 is renamed to exec");
188188
189 fn collectOutputPosix(
190 child: *const ChildProcess,
191 stdout: *std.ArrayList(u8),
192 stderr: *std.ArrayList(u8),
193 max_output_bytes: usize,
194 ) !void {
195 var poll_fds = [_]os.pollfd{
196 .{ .fd = child.stdout.?.handle, .events = os.POLLIN, .revents = undefined },
197 .{ .fd = child.stderr.?.handle, .events = os.POLLIN, .revents = undefined },
198 };
199
200 var dead_fds: usize = 0;
201 // We ask for ensureCapacity with this much extra space. This has more of an
202 // effect on small reads because once the reads start to get larger the amount
203 // of space an ArrayList will allocate grows exponentially.
204 const bump_amt = 512;
205
206 while (dead_fds < poll_fds.len) {
207 const events = try os.poll(&poll_fds, std.math.maxInt(i32));
208 if (events == 0) continue;
209
210 // Try reading whatever is available before checking the error
211 // conditions.
212 if (poll_fds[0].revents & os.POLLIN != 0) {
213 // stdout is ready.
214 const new_capacity = std.math.min(stdout.items.len + bump_amt, max_output_bytes);
215 if (new_capacity == stdout.capacity) return error.StdoutStreamTooLong;
216 try stdout.ensureCapacity(new_capacity);
217 stdout.items.len += try os.read(poll_fds[0].fd, stdout.unusedCapacitySlice());
218 }
219 if (poll_fds[1].revents & os.POLLIN != 0) {
220 // stderr is ready.
221 const new_capacity = std.math.min(stderr.items.len + bump_amt, max_output_bytes);
222 if (new_capacity == stderr.capacity) return error.StderrStreamTooLong;
223 try stderr.ensureCapacity(new_capacity);
224 stderr.items.len += try os.read(poll_fds[1].fd, stderr.unusedCapacitySlice());
225 }
226
227 // Exclude the fds that signaled an error.
228 if (poll_fds[0].revents & (os.POLLERR | os.POLLNVAL | os.POLLHUP) != 0) {
229 poll_fds[0].fd = -1;
230 dead_fds += 1;
231 }
232 if (poll_fds[1].revents & (os.POLLERR | os.POLLNVAL | os.POLLHUP) != 0) {
233 poll_fds[1].fd = -1;
234 dead_fds += 1;
235 }
236 }
237 }
238
189239 /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
190240 /// If it succeeds, the caller owns result.stdout and result.stderr memory.
191241 pub fn exec(args: struct {
......@@ -210,19 +260,33 @@ pub const ChildProcess = struct {
210260
211261 try child.spawn();
212262
213 const stdout_in = child.stdout.?.reader();
214 const stderr_in = child.stderr.?.reader();
263 // TODO collect output in a deadlock-avoiding way on Windows.
264 // https://github.com/ziglang/zig/issues/6343
265 if (builtin.os.tag == .windows) {
266 const stdout_in = child.stdout.?.reader();
267 const stderr_in = child.stderr.?.reader();
268
269 const stdout = try stdout_in.readAllAlloc(args.allocator, args.max_output_bytes);
270 errdefer args.allocator.free(stdout);
271 const stderr = try stderr_in.readAllAlloc(args.allocator, args.max_output_bytes);
272 errdefer args.allocator.free(stderr);
273
274 return ExecResult{
275 .term = try child.wait(),
276 .stdout = stdout,
277 .stderr = stderr,
278 };
279 }
280
281 var stdout = std.ArrayList(u8).init(args.allocator);
282 var stderr = std.ArrayList(u8).init(args.allocator);
215283
216 // TODO https://github.com/ziglang/zig/issues/6343
217 const stdout = try stdout_in.readAllAlloc(args.allocator, args.max_output_bytes);
218 errdefer args.allocator.free(stdout);
219 const stderr = try stderr_in.readAllAlloc(args.allocator, args.max_output_bytes);
220 errdefer args.allocator.free(stderr);
284 try collectOutputPosix(child, &stdout, &stderr, args.max_output_bytes);
221285
222286 return ExecResult{
223287 .term = try child.wait(),
224 .stdout = stdout,
225 .stderr = stderr,
288 .stdout = stdout.toOwnedSlice(),
289 .stderr = stderr.toOwnedSlice(),
226290 };
227291 }
228292
lib/std/os.zig+2-1
......@@ -5269,7 +5269,8 @@ pub const PollError = error{
52695269
52705270pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
52715271 while (true) {
5272 const rc = system.poll(fds.ptr, fds.len, timeout);
5272 const fds_count = math.cast(nfds_t, fds.len) catch return error.SystemResources;
5273 const rc = system.poll(fds.ptr, fds_count, timeout);
52735274 if (builtin.os.tag == .windows) {
52745275 if (rc == windows.ws2_32.SOCKET_ERROR) {
52755276 switch (windows.ws2_32.WSAGetLastError()) {
lib/std/os/bits/darwin.zig+1-1
......@@ -1461,7 +1461,7 @@ pub const LOCK_EX = 2;
14611461pub const LOCK_UN = 8;
14621462pub const LOCK_NB = 4;
14631463
1464pub const nfds_t = usize;
1464pub const nfds_t = u32;
14651465pub const pollfd = extern struct {
14661466 fd: fd_t,
14671467 events: i16,
lib/std/os/bits/freebsd.zig+34
......@@ -1480,3 +1480,37 @@ pub const rlimit = extern struct {
14801480pub const SHUT_RD = 0;
14811481pub const SHUT_WR = 1;
14821482pub const SHUT_RDWR = 2;
1483
1484pub const nfds_t = u32;
1485
1486pub const pollfd = extern struct {
1487 fd: fd_t,
1488 events: i16,
1489 revents: i16,
1490};
1491
1492/// any readable data available.
1493pub const POLLIN = 0x0001;
1494/// OOB/Urgent readable data.
1495pub const POLLPRI = 0x0002;
1496/// file descriptor is writeable.
1497pub const POLLOUT = 0x0004;
1498/// non-OOB/URG data available.
1499pub const POLLRDNORM = 0x0040;
1500/// no write type differentiation.
1501pub const POLLWRNORM = POLLOUT;
1502/// OOB/Urgent readable data.
1503pub const POLLRDBAND = 0x0080;
1504/// OOB/Urgent data can be written.
1505pub const POLLWRBAND = 0x0100;
1506/// like POLLIN, except ignore EOF.
1507pub const POLLINIGNEOF = 0x2000;
1508/// some poll error occurred.
1509pub const POLLERR = 0x0008;
1510/// file descriptor was "hung up".
1511pub const POLLHUP = 0x0010;
1512/// requested events "invalid".
1513pub const POLLNVAL = 0x0020;
1514
1515pub const POLLSTANDARD = POLLIN | POLLPRI | POLLOUT | POLLRDNORM | POLLRDBAND |
1516 POLLWRBAND | POLLERR | POLLHUP | POLLNVAL;