| ... | ... | @@ -280,22 +280,38 @@ pub const ChildProcess = struct { |
| 280 | 280 | } |
| 281 | 281 | |
| 282 | 282 | fn cleanupAfterWait(self: *ChildProcess, status: u32) !Term { |
| 283 | | defer { |
| 284 | | os.close(self.err_pipe[0]); |
| 285 | | os.close(self.err_pipe[1]); |
| 286 | | } |
| 287 | | |
| 288 | | // Write maxInt(ErrInt) to the write end of the err_pipe. This is after |
| 289 | | // waitpid, so this write is guaranteed to be after the child |
| 290 | | // pid potentially wrote an error. This way we can do a blocking |
| 291 | | // read on the error pipe and either get maxInt(ErrInt) (no error) or |
| 292 | | // an error code. |
| 293 | | try writeIntFd(self.err_pipe[1], maxInt(ErrInt)); |
| 294 | | const err_int = try readIntFd(self.err_pipe[0]); |
| 295 | | // Here we potentially return the fork child's error |
| 296 | | // from the parent pid. |
| 297 | | if (err_int != maxInt(ErrInt)) { |
| 298 | | return @errSetCast(SpawnError, @intToError(err_int)); |
| 283 | defer destroyPipe(self.err_pipe); |
| 284 | |
| 285 | if (builtin.os == .linux) { |
| 286 | var fd = [1]std.os.pollfd{std.os.pollfd{ |
| 287 | .fd = self.err_pipe[0], |
| 288 | .events = std.os.POLLIN, |
| 289 | .revents = undefined, |
| 290 | }}; |
| 291 | |
| 292 | // Check if the eventfd buffer stores a non-zero value by polling |
| 293 | // it, that's the error code returned by the child process. |
| 294 | _ = std.os.poll(&fd, 0) catch unreachable; |
| 295 | |
| 296 | // According to eventfd(2) the descriptro is readable if the counter |
| 297 | // has a value greater than 0 |
| 298 | if ((fd[0].revents & std.os.POLLIN) != 0) { |
| 299 | const err_int = try readIntFd(self.err_pipe[0]); |
| 300 | return @errSetCast(SpawnError, @intToError(err_int)); |
| 301 | } |
| 302 | } else { |
| 303 | // Write maxInt(ErrInt) to the write end of the err_pipe. This is after |
| 304 | // waitpid, so this write is guaranteed to be after the child |
| 305 | // pid potentially wrote an error. This way we can do a blocking |
| 306 | // read on the error pipe and either get maxInt(ErrInt) (no error) or |
| 307 | // an error code. |
| 308 | try writeIntFd(self.err_pipe[1], maxInt(ErrInt)); |
| 309 | const err_int = try readIntFd(self.err_pipe[0]); |
| 310 | // Here we potentially return the fork child's error from the parent |
| 311 | // pid. |
| 312 | if (err_int != maxInt(ErrInt)) { |
| 313 | return @errSetCast(SpawnError, @intToError(err_int)); |
| 314 | } |
| 299 | 315 | } |
| 300 | 316 | |
| 301 | 317 | return statusToTerm(status); |
| ... | ... | @@ -359,7 +375,16 @@ pub const ChildProcess = struct { |
| 359 | 375 | |
| 360 | 376 | // This pipe is used to communicate errors between the time of fork |
| 361 | 377 | // and execve from the child process to the parent process. |
| 362 | | const err_pipe = try os.pipe(); |
| 378 | const err_pipe = blk: { |
| 379 | if (builtin.os == .linux) { |
| 380 | const fd = try os.eventfd(0, 0); |
| 381 | // There's no distinction between the readable and the writeable |
| 382 | // end with eventfd |
| 383 | break :blk [2]os.fd_t{ fd, fd }; |
| 384 | } else { |
| 385 | break :blk try os.pipe(); |
| 386 | } |
| 387 | }; |
| 363 | 388 | errdefer destroyPipe(err_pipe); |
| 364 | 389 | |
| 365 | 390 | const pid_result = try os.fork(); |
| ... | ... | @@ -773,7 +798,7 @@ fn windowsMakePipeOut(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const |
| 773 | 798 | |
| 774 | 799 | fn destroyPipe(pipe: [2]os.fd_t) void { |
| 775 | 800 | os.close(pipe[0]); |
| 776 | | os.close(pipe[1]); |
| 801 | if (pipe[0] != pipe[1]) os.close(pipe[1]); |
| 777 | 802 | } |
| 778 | 803 | |
| 779 | 804 | // Child of fork calls this to report an error to the fork parent. |
| ... | ... | @@ -787,12 +812,12 @@ const ErrInt = @IntType(false, @sizeOf(anyerror) * 8); |
| 787 | 812 | |
| 788 | 813 | fn writeIntFd(fd: i32, value: ErrInt) !void { |
| 789 | 814 | const stream = &File.openHandle(fd).outStream().stream; |
| 790 | | stream.writeIntNative(ErrInt, value) catch return error.SystemResources; |
| 815 | stream.writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources; |
| 791 | 816 | } |
| 792 | 817 | |
| 793 | 818 | fn readIntFd(fd: i32) !ErrInt { |
| 794 | 819 | const stream = &File.openHandle(fd).inStream().stream; |
| 795 | | return stream.readIntNative(ErrInt) catch return error.SystemResources; |
| 820 | return @intCast(ErrInt, stream.readIntNative(u64) catch return error.SystemResources); |
| 796 | 821 | } |
| 797 | 822 | |
| 798 | 823 | /// Caller must free result. |