| author | |
| committer | |
| log | e54fd2578195ce7857d921ac22b800d376fb870b |
| tree | bcfdeec3c406aeb2a0427d816d0f3d242354fdce |
| parent | 1590ed9d6aea95e5a21e3455e8edba4cdb374f2c |
| parent | 717cf00fe0d68dc1213fb645b184afe1cbf52104 |
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 { | ... | @@ -337,11 +337,21 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 337 | return self.pop(); | 337 | return self.pop(); |
| 338 | } | 338 | } |
| 339 | 339 | ||
| 340 | // For a nicer API, `items.len` is the length, not the capacity. | 340 | /// Returns a slice of all the items plus the extra capacity, whose memory |
| 341 | // This requires "unsafe" slicing. | 341 | /// contents are undefined. |
| 342 | fn allocatedSlice(self: Self) Slice { | 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. | ||
| 343 | return self.items.ptr[0..self.capacity]; | 345 | return self.items.ptr[0..self.capacity]; |
| 344 | } | 346 | } |
| 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 | } | ||
| 345 | }; | 355 | }; |
| 346 | } | 356 | } |
| 347 | 357 |
lib/std/child_process.zig+73-9| ... | @@ -186,6 +186,56 @@ pub const ChildProcess = struct { | ... | @@ -186,6 +186,56 @@ pub const ChildProcess = struct { |
| 186 | 186 | ||
| 187 | pub const exec2 = @compileError("deprecated: exec2 is renamed to exec"); | 187 | pub const exec2 = @compileError("deprecated: exec2 is renamed to exec"); |
| 188 | 188 | ||
| 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 | |||
| 189 | /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. | 239 | /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. |
| 190 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. | 240 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. |
| 191 | pub fn exec(args: struct { | 241 | pub fn exec(args: struct { |
| ... | @@ -210,19 +260,33 @@ pub const ChildProcess = struct { | ... | @@ -210,19 +260,33 @@ pub const ChildProcess = struct { |
| 210 | 260 | ||
| 211 | try child.spawn(); | 261 | try child.spawn(); |
| 212 | 262 | ||
| 213 | const stdout_in = child.stdout.?.reader(); | 263 | // TODO collect output in a deadlock-avoiding way on Windows. |
| 214 | const stderr_in = child.stderr.?.reader(); | 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); | ||
| 215 | 283 | ||
| 216 | // TODO https://github.com/ziglang/zig/issues/6343 | 284 | try collectOutputPosix(child, &stdout, &stderr, args.max_output_bytes); |
| 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); | ||
| 221 | 285 | ||
| 222 | return ExecResult{ | 286 | return ExecResult{ |
| 223 | .term = try child.wait(), | 287 | .term = try child.wait(), |
| 224 | .stdout = stdout, | 288 | .stdout = stdout.toOwnedSlice(), |
| 225 | .stderr = stderr, | 289 | .stderr = stderr.toOwnedSlice(), |
| 226 | }; | 290 | }; |
| 227 | } | 291 | } |
| 228 | 292 |
lib/std/os.zig+2-1| ... | @@ -5269,7 +5269,8 @@ pub const PollError = error{ | ... | @@ -5269,7 +5269,8 @@ pub const PollError = error{ |
| 5269 | 5269 | ||
| 5270 | pub fn poll(fds: []pollfd, timeout: i32) PollError!usize { | 5270 | pub fn poll(fds: []pollfd, timeout: i32) PollError!usize { |
| 5271 | while (true) { | 5271 | 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); | ||
| 5273 | if (builtin.os.tag == .windows) { | 5274 | if (builtin.os.tag == .windows) { |
| 5274 | if (rc == windows.ws2_32.SOCKET_ERROR) { | 5275 | if (rc == windows.ws2_32.SOCKET_ERROR) { |
| 5275 | switch (windows.ws2_32.WSAGetLastError()) { | 5276 | switch (windows.ws2_32.WSAGetLastError()) { |
lib/std/os/bits/darwin.zig+1-1| ... | @@ -1461,7 +1461,7 @@ pub const LOCK_EX = 2; | ... | @@ -1461,7 +1461,7 @@ pub const LOCK_EX = 2; |
| 1461 | pub const LOCK_UN = 8; | 1461 | pub const LOCK_UN = 8; |
| 1462 | pub const LOCK_NB = 4; | 1462 | pub const LOCK_NB = 4; |
| 1463 | 1463 | ||
| 1464 | pub const nfds_t = usize; | 1464 | pub const nfds_t = u32; |
| 1465 | pub const pollfd = extern struct { | 1465 | pub const pollfd = extern struct { |
| 1466 | fd: fd_t, | 1466 | fd: fd_t, |
| 1467 | events: i16, | 1467 | events: i16, |
lib/std/os/bits/freebsd.zig+34| ... | @@ -1480,3 +1480,37 @@ pub const rlimit = extern struct { | ... | @@ -1480,3 +1480,37 @@ pub const rlimit = extern struct { |
| 1480 | pub const SHUT_RD = 0; | 1480 | pub const SHUT_RD = 0; |
| 1481 | pub const SHUT_WR = 1; | 1481 | pub const SHUT_WR = 1; |
| 1482 | pub const SHUT_RDWR = 2; | 1482 | pub const SHUT_RDWR = 2; |
| 1483 | |||
| 1484 | pub const nfds_t = u32; | ||
| 1485 | |||
| 1486 | pub const pollfd = extern struct { | ||
| 1487 | fd: fd_t, | ||
| 1488 | events: i16, | ||
| 1489 | revents: i16, | ||
| 1490 | }; | ||
| 1491 | |||
| 1492 | /// any readable data available. | ||
| 1493 | pub const POLLIN = 0x0001; | ||
| 1494 | /// OOB/Urgent readable data. | ||
| 1495 | pub const POLLPRI = 0x0002; | ||
| 1496 | /// file descriptor is writeable. | ||
| 1497 | pub const POLLOUT = 0x0004; | ||
| 1498 | /// non-OOB/URG data available. | ||
| 1499 | pub const POLLRDNORM = 0x0040; | ||
| 1500 | /// no write type differentiation. | ||
| 1501 | pub const POLLWRNORM = POLLOUT; | ||
| 1502 | /// OOB/Urgent readable data. | ||
| 1503 | pub const POLLRDBAND = 0x0080; | ||
| 1504 | /// OOB/Urgent data can be written. | ||
| 1505 | pub const POLLWRBAND = 0x0100; | ||
| 1506 | /// like POLLIN, except ignore EOF. | ||
| 1507 | pub const POLLINIGNEOF = 0x2000; | ||
| 1508 | /// some poll error occurred. | ||
| 1509 | pub const POLLERR = 0x0008; | ||
| 1510 | /// file descriptor was "hung up". | ||
| 1511 | pub const POLLHUP = 0x0010; | ||
| 1512 | /// requested events "invalid". | ||
| 1513 | pub const POLLNVAL = 0x0020; | ||
| 1514 | |||
| 1515 | pub const POLLSTANDARD = POLLIN | POLLPRI | POLLOUT | POLLRDNORM | POLLRDBAND | | ||
| 1516 | POLLWRBAND | POLLERR | POLLHUP | POLLNVAL; |