Commit 389181f6be
Changed files (1)
lib
std
process
lib/std/process/Child.zig
@@ -526,7 +526,18 @@ fn statusToTerm(status: u32) Term {
}
fn spawnPosix(self: *ChildProcess) SpawnError!void {
- const pipe_flags: posix.O = .{};
+ // The child process does need to access (one end of) these pipes. However,
+ // we must initially set CLOEXEC to avoid a race condition. If another thread
+ // is racing to spawn a different child process, we don't want it to inherit
+ // these FDs in any scenario; that would mean that, for instance, calls to
+ // `poll` from the parent would not report the child's stdout as closing when
+ // expected, since the other child may retain a reference to the write end of
+ // the pipe. So, we create the pipes with CLOEXEC initially. After fork, we
+ // need to do something in the new child to make sure we preserve the reference
+ // we want. We could use `fcntl` to remove CLOEXEC from the FD, but as it
+ // turns out, we `dup2` everything anyway, so there's no need!
+ const pipe_flags: posix.O = .{ .CLOEXEC = true };
+
const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try posix.pipe2(pipe_flags) else undefined;
errdefer if (self.stdin_behavior == StdIo.Pipe) {
destroyPipe(stdin_pipe);
@@ -614,19 +625,6 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
- if (self.stdin_behavior == .Pipe) {
- posix.close(stdin_pipe[0]);
- posix.close(stdin_pipe[1]);
- }
- if (self.stdout_behavior == .Pipe) {
- posix.close(stdout_pipe[0]);
- posix.close(stdout_pipe[1]);
- }
- if (self.stderr_behavior == .Pipe) {
- posix.close(stderr_pipe[0]);
- posix.close(stderr_pipe[1]);
- }
-
if (self.cwd_dir) |cwd| {
posix.fchdir(cwd.fd) catch |err| forkChildErrReport(err_pipe[1], err);
} else if (self.cwd) |cwd| {