From 46658257f458b7c3c95d7e10cbde85403f7bdb44 Mon Sep 17 00:00:00 2001 From: breakmit Date: Fri, 6 Mar 2026 04:51:28 +0100 Subject: [PATCH] Io.Threaded.spawnPosix: implement passing file descriptors as stdio (#31379) `std.process.spawn`: remove the TODO for nonblocking file stdio and document the behavior. Fix a bug in Io.Uring.dup2 where the function does not return on success Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31379 Reviewed-by: Andrew Kelley Co-authored-by: breakmit Co-committed-by: breakmit --- lib/std/Io/Dispatch.zig | 5 +---- lib/std/Io/Threaded.zig | 2 +- lib/std/Io/Uring.zig | 7 ++----- lib/std/process.zig | 6 ++++++ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/std/Io/Dispatch.zig b/lib/std/Io/Dispatch.zig index 1a1d644276..947e8e3070 100644 --- a/lib/std/Io/Dispatch.zig +++ b/lib/std/Io/Dispatch.zig @@ -4404,10 +4404,7 @@ fn setUpChildIo( .close => closeFd(std_fileno), .inherit => {}, .ignore => try ev.dup2(dev_null_fd, std_fileno), - .file => |file| { - if (file.flags.nonblocking) @panic("TODO implement setUpChildIo when nonblocking file is used"); - try ev.dup2(file.handle, std_fileno); - }, + .file => |file| try ev.dup2(file.handle, std_fileno), } } diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 83bda2c865..b1804c4605 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -15659,7 +15659,7 @@ fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32 .close => closeFd(std_fileno), .inherit => {}, .ignore => try dup2(dev_null_fd, std_fileno), - .file => @panic("TODO implement setUpChildIo when file is used"), + .file => |file| try dup2(file.handle, std_fileno), } } diff --git a/lib/std/Io/Uring.zig b/lib/std/Io/Uring.zig index 0f4f64334f..6031b1b298 100644 --- a/lib/std/Io/Uring.zig +++ b/lib/std/Io/Uring.zig @@ -4550,10 +4550,7 @@ fn setUpChildIo( .close => _ = linux.close(std_fileno), .inherit => {}, .ignore => try dup2(sync, dev_null_fd, std_fileno), - .file => |file| { - if (file.flags.nonblocking) @panic("TODO implement setUpChildIo when nonblocking file is used"); - try dup2(sync, file.handle, std_fileno); - }, + .file => |file| try dup2(sync, file.handle, std_fileno), } } @@ -4565,7 +4562,7 @@ pub fn dup2(sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t) DupError!void while (true) { try sync.cancel_region.await(.nothing); switch (linux.errno(linux.dup2(old_fd, new_fd))) { - .SUCCESS => {}, + .SUCCESS => return, .BUSY, .INTR => {}, .INVAL => |err| return errnoBug(err), // invalid parameters .BADF => |err| return errnoBug(err), // use after free diff --git a/lib/std/process.zig b/lib/std/process.zig index 1be0cd628d..701eb434b5 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -409,6 +409,12 @@ pub const SpawnOptions = struct { /// Inherit the corresponding stream from the parent process. inherit, /// Pass an already open file from the parent to the child. + /// + /// Nonblocking mode will be kept in the child process if present. This is + /// likely not supported by the child process. For example: + /// - Zig's std.Io.File.stdout() assumes blocking mode + /// - Rust explicity documents that nonblocking stdio may cause panics + /// - C++ standard streams do not support nonblocking file descriptors file: File, /// Pass a null stream to the child process by opening "/dev/null" on POSIX /// and "NUL" on Windows.