| ... | ... | @@ -73,7 +73,7 @@ cwd: ?[]const u8, |
| 73 | 73 | /// Once that is done, `cwd` will be deprecated in favor of this field. |
| 74 | 74 | cwd_dir: ?fs.Dir = null, |
| 75 | 75 | |
| 76 | | err_pipe: ?if (native_os == .windows) void else [2]posix.fd_t, |
| 76 | err_pipe: if (native_os == .windows) void else ?posix.fd_t, |
| 77 | 77 | |
| 78 | 78 | expand_arg0: Arg0Expand, |
| 79 | 79 | |
| ... | ... | @@ -211,7 +211,7 @@ pub fn init(argv: []const []const u8, allocator: mem.Allocator) ChildProcess { |
| 211 | 211 | .argv = argv, |
| 212 | 212 | .id = undefined, |
| 213 | 213 | .thread_handle = undefined, |
| 214 | | .err_pipe = null, |
| 214 | .err_pipe = if (native_os == .windows) {} else null, |
| 215 | 215 | .term = null, |
| 216 | 216 | .env_map = null, |
| 217 | 217 | .cwd = null, |
| ... | ... | @@ -293,17 +293,49 @@ pub fn killPosix(self: *ChildProcess) !Term { |
| 293 | 293 | error.ProcessNotFound => return error.AlreadyTerminated, |
| 294 | 294 | else => return err, |
| 295 | 295 | }; |
| 296 | | self.waitUnwrapped(); |
| 296 | self.waitUnwrappedPosix(); |
| 297 | 297 | return self.term.?; |
| 298 | 298 | } |
| 299 | 299 | |
| 300 | 300 | pub const WaitError = SpawnError || std.os.windows.GetProcessMemoryInfoError; |
| 301 | 301 | |
| 302 | /// On some targets, `spawn` may not report all spawn errors, such as `error.InvalidExe`. |
| 303 | /// This function will block until any spawn errors can be reported, and return them. |
| 304 | pub fn waitForSpawn(self: *ChildProcess) SpawnError!void { |
| 305 | if (native_os == .windows) return; // `spawn` reports everything |
| 306 | if (self.term) |term| { |
| 307 | _ = term catch |spawn_err| return spawn_err; |
| 308 | return; |
| 309 | } |
| 310 | |
| 311 | const err_pipe = self.err_pipe orelse return; |
| 312 | self.err_pipe = null; |
| 313 | |
| 314 | // Wait for the child to report any errors in or before `execvpe`. |
| 315 | if (readIntFd(err_pipe)) |child_err_int| { |
| 316 | posix.close(err_pipe); |
| 317 | const child_err: SpawnError = @errorCast(@errorFromInt(child_err_int)); |
| 318 | self.term = child_err; |
| 319 | return child_err; |
| 320 | } else |_| { |
| 321 | // Write end closed by CLOEXEC at the time of the `execvpe` call, indicating success! |
| 322 | posix.close(err_pipe); |
| 323 | } |
| 324 | } |
| 325 | |
| 302 | 326 | /// Blocks until child process terminates and then cleans up all resources. |
| 303 | 327 | pub fn wait(self: *ChildProcess) WaitError!Term { |
| 304 | | const term = if (native_os == .windows) try self.waitWindows() else self.waitPosix(); |
| 328 | try self.waitForSpawn(); // report spawn errors |
| 329 | if (self.term) |term| { |
| 330 | self.cleanupStreams(); |
| 331 | return term; |
| 332 | } |
| 333 | switch (native_os) { |
| 334 | .windows => try self.waitUnwrappedWindows(), |
| 335 | else => self.waitUnwrappedPosix(), |
| 336 | } |
| 305 | 337 | self.id = undefined; |
| 306 | | return term; |
| 338 | return self.term.?; |
| 307 | 339 | } |
| 308 | 340 | |
| 309 | 341 | pub const RunResult = struct { |
| ... | ... | @@ -405,26 +437,6 @@ pub fn run(args: struct { |
| 405 | 437 | }; |
| 406 | 438 | } |
| 407 | 439 | |
| 408 | | fn waitWindows(self: *ChildProcess) WaitError!Term { |
| 409 | | if (self.term) |term| { |
| 410 | | self.cleanupStreams(); |
| 411 | | return term; |
| 412 | | } |
| 413 | | |
| 414 | | try self.waitUnwrappedWindows(); |
| 415 | | return self.term.?; |
| 416 | | } |
| 417 | | |
| 418 | | fn waitPosix(self: *ChildProcess) SpawnError!Term { |
| 419 | | if (self.term) |term| { |
| 420 | | self.cleanupStreams(); |
| 421 | | return term; |
| 422 | | } |
| 423 | | |
| 424 | | self.waitUnwrapped(); |
| 425 | | return self.term.?; |
| 426 | | } |
| 427 | | |
| 428 | 440 | fn waitUnwrappedWindows(self: *ChildProcess) WaitError!void { |
| 429 | 441 | const result = windows.WaitForSingleObjectEx(self.id, windows.INFINITE, false); |
| 430 | 442 | |
| ... | ... | @@ -447,7 +459,7 @@ fn waitUnwrappedWindows(self: *ChildProcess) WaitError!void { |
| 447 | 459 | return result; |
| 448 | 460 | } |
| 449 | 461 | |
| 450 | | fn waitUnwrapped(self: *ChildProcess) void { |
| 462 | fn waitUnwrappedPosix(self: *ChildProcess) void { |
| 451 | 463 | const res: posix.WaitPidResult = res: { |
| 452 | 464 | if (self.request_resource_usage_statistics) { |
| 453 | 465 | switch (native_os) { |
| ... | ... | @@ -469,7 +481,7 @@ fn waitUnwrapped(self: *ChildProcess) void { |
| 469 | 481 | } |
| 470 | 482 | |
| 471 | 483 | fn handleWaitResult(self: *ChildProcess, status: u32) void { |
| 472 | | self.term = self.cleanupAfterWait(status); |
| 484 | self.term = statusToTerm(status); |
| 473 | 485 | } |
| 474 | 486 | |
| 475 | 487 | fn cleanupStreams(self: *ChildProcess) void { |
| ... | ... | @@ -487,46 +499,6 @@ fn cleanupStreams(self: *ChildProcess) void { |
| 487 | 499 | } |
| 488 | 500 | } |
| 489 | 501 | |
| 490 | | fn cleanupAfterWait(self: *ChildProcess, status: u32) !Term { |
| 491 | | if (self.err_pipe) |err_pipe| { |
| 492 | | defer destroyPipe(err_pipe); |
| 493 | | |
| 494 | | if (native_os == .linux) { |
| 495 | | var fd = [1]posix.pollfd{posix.pollfd{ |
| 496 | | .fd = err_pipe[0], |
| 497 | | .events = posix.POLL.IN, |
| 498 | | .revents = undefined, |
| 499 | | }}; |
| 500 | | |
| 501 | | // Check if the eventfd buffer stores a non-zero value by polling |
| 502 | | // it, that's the error code returned by the child process. |
| 503 | | _ = posix.poll(&fd, 0) catch unreachable; |
| 504 | | |
| 505 | | // According to eventfd(2) the descriptor is readable if the counter |
| 506 | | // has a value greater than 0 |
| 507 | | if ((fd[0].revents & posix.POLL.IN) != 0) { |
| 508 | | const err_int = try readIntFd(err_pipe[0]); |
| 509 | | return @as(SpawnError, @errorCast(@errorFromInt(err_int))); |
| 510 | | } |
| 511 | | } else { |
| 512 | | // Write maxInt(ErrInt) to the write end of the err_pipe. This is after |
| 513 | | // waitpid, so this write is guaranteed to be after the child |
| 514 | | // pid potentially wrote an error. This way we can do a blocking |
| 515 | | // read on the error pipe and either get maxInt(ErrInt) (no error) or |
| 516 | | // an error code. |
| 517 | | try writeIntFd(err_pipe[1], maxInt(ErrInt)); |
| 518 | | const err_int = try readIntFd(err_pipe[0]); |
| 519 | | // Here we potentially return the fork child's error from the parent |
| 520 | | // pid. |
| 521 | | if (err_int != maxInt(ErrInt)) { |
| 522 | | return @as(SpawnError, @errorCast(@errorFromInt(err_int))); |
| 523 | | } |
| 524 | | } |
| 525 | | } |
| 526 | | |
| 527 | | return statusToTerm(status); |
| 528 | | } |
| 529 | | |
| 530 | 502 | fn statusToTerm(status: u32) Term { |
| 531 | 503 | return if (posix.W.IFEXITED(status)) |
| 532 | 504 | Term{ .Exited = posix.W.EXITSTATUS(status) } |
| ... | ... | @@ -636,18 +608,9 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void { |
| 636 | 608 | } |
| 637 | 609 | }; |
| 638 | 610 | |
| 639 | | // This pipe is used to communicate errors between the time of fork |
| 640 | | // and execve from the child process to the parent process. |
| 641 | | const err_pipe = blk: { |
| 642 | | if (native_os == .linux) { |
| 643 | | const fd = try posix.eventfd(0, linux.EFD.CLOEXEC); |
| 644 | | // There's no distinction between the readable and the writeable |
| 645 | | // end with eventfd |
| 646 | | break :blk [2]posix.fd_t{ fd, fd }; |
| 647 | | } else { |
| 648 | | break :blk try posix.pipe2(.{ .CLOEXEC = true }); |
| 649 | | } |
| 650 | | }; |
| 611 | // This pipe communicates to the parent errors in the child between `fork` and `execvpe`. |
| 612 | // It is closed by the child (via CLOEXEC) without writing if `execvpe` succeeds. |
| 613 | const err_pipe: [2]posix.fd_t = try posix.pipe2(.{ .CLOEXEC = true }); |
| 651 | 614 | errdefer destroyPipe(err_pipe); |
| 652 | 615 | |
| 653 | 616 | const pid_result = try posix.fork(); |
| ... | ... | @@ -687,6 +650,11 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void { |
| 687 | 650 | } |
| 688 | 651 | |
| 689 | 652 | // we are the parent |
| 653 | errdefer comptime unreachable; // The child is forked; we must not error from now on |
| 654 | |
| 655 | posix.close(err_pipe[1]); // make sure only the child holds the write end open |
| 656 | self.err_pipe = err_pipe[0]; |
| 657 | |
| 690 | 658 | const pid: i32 = @intCast(pid_result); |
| 691 | 659 | if (self.stdin_behavior == .Pipe) { |
| 692 | 660 | self.stdin = .{ .handle = stdin_pipe[1] }; |
| ... | ... | @@ -705,7 +673,6 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void { |
| 705 | 673 | } |
| 706 | 674 | |
| 707 | 675 | self.id = pid; |
| 708 | | self.err_pipe = err_pipe; |
| 709 | 676 | self.term = null; |
| 710 | 677 | |
| 711 | 678 | if (self.stdin_behavior == .Pipe) { |