Skip to content

Commit ff775b2

Browse files
bartlomiejuclaude
andauthored
fix(ext/node): support numeric FDs in child_process stdio array (#33140)
## Summary Now that `fs.openSync` returns real OS file descriptors (#33039), numeric values in child_process stdio arrays should be treated as raw fds rather than Deno resource IDs. This re-lands the functionality from #32959 (reverted in #33017) with the correct fd-based approach. - Rename `StdioOrRid` to `StdioOrFd`, replace `Rid(ResourceId)` with `Fd(i32)` - `as_stdio()` dups the fd directly (`libc::dup` on Unix, handle clone on Windows) instead of looking up the resource table - `extra_stdio` now accepts `StdioOrFd` so numeric fds work beyond stdin/stdout/stderr - Hardcoded `Rid(1)`/`Rid(2)` for inherit become `Fd(1)`/`Fd(2)` - Remove unused `FileResource` import --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d198cda commit ff775b2

File tree

4 files changed

+217
-106
lines changed

4 files changed

+217
-106
lines changed

ext/io/lib.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -262,31 +262,50 @@ deno_core::extension!(deno_io,
262262
));
263263
assert_eq!(rid, 0, "stdin must have ResourceId 0");
264264

265-
let rid = t.add(FileResource::new(
266-
Rc::new(match stdio.stdout.pipe {
267-
StdioPipeInner::Inherit => StdFileResourceInner::new(
265+
let (stdout_inner, child_stdout) = match stdio.stdout.pipe {
266+
StdioPipeInner::Inherit => (
267+
StdFileResourceInner::new(
268268
StdFileResourceKind::Stdout,
269269
STDOUT_HANDLE.try_clone().unwrap(),
270270
None,
271271
),
272-
StdioPipeInner::File(pipe) => StdFileResourceInner::file(pipe, None),
273-
}),
272+
STDOUT_HANDLE.try_clone().unwrap(),
273+
),
274+
StdioPipeInner::File(pipe) => {
275+
let child_handle = pipe.try_clone().unwrap();
276+
(StdFileResourceInner::file(pipe, None), child_handle)
277+
}
278+
};
279+
let rid = t.add(FileResource::new(
280+
Rc::new(stdout_inner),
274281
"stdout".to_string(),
275282
));
276283
assert_eq!(rid, 1, "stdout must have ResourceId 1");
277284

278-
let rid = t.add(FileResource::new(
279-
Rc::new(match stdio.stderr.pipe {
280-
StdioPipeInner::Inherit => StdFileResourceInner::new(
285+
let (stderr_inner, child_stderr) = match stdio.stderr.pipe {
286+
StdioPipeInner::Inherit => (
287+
StdFileResourceInner::new(
281288
StdFileResourceKind::Stderr,
282289
STDERR_HANDLE.try_clone().unwrap(),
283290
None,
284291
),
285-
StdioPipeInner::File(pipe) => StdFileResourceInner::file(pipe, None),
286-
}),
292+
STDERR_HANDLE.try_clone().unwrap(),
293+
),
294+
StdioPipeInner::File(pipe) => {
295+
let child_handle = pipe.try_clone().unwrap();
296+
(StdFileResourceInner::file(pipe, None), child_handle)
297+
}
298+
};
299+
let rid = t.add(FileResource::new(
300+
Rc::new(stderr_inner),
287301
"stderr".to_string(),
288302
));
289303
assert_eq!(rid, 2, "stderr must have ResourceId 2");
304+
305+
state.put(ChildProcessStdio {
306+
stdout: child_stdout,
307+
stderr: child_stderr,
308+
});
290309
}
291310
},
292311
);
@@ -339,6 +358,18 @@ pub struct Stdio {
339358
pub stderr: StdioPipe,
340359
}
341360

361+
/// Holds the effective stdout/stderr handles for child process inheritance.
362+
///
363+
/// When the runtime redirects stdout/stderr (e.g. during `deno test` for
364+
/// output capture), child processes spawned with `stdio: "inherit"` need
365+
/// to inherit the redirected handles, not the original OS stdout/stderr.
366+
/// This struct is stored in `OpState` during IO extension init and read
367+
/// by the process extension when spawning children.
368+
pub struct ChildProcessStdio {
369+
pub stdout: StdFile,
370+
pub stderr: StdFile,
371+
}
372+
342373
#[derive(Debug)]
343374
pub struct WriteOnlyResource<S> {
344375
stream: AsyncRefCell<S>,

ext/node/polyfills/internal/child_process.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,7 @@ function toDenoStdio(
748748
return "inherit";
749749
}
750750
if (typeof pipe === "number") {
751-
/* Assume it's a rid returned by fs APIs */
751+
/* Real OS file descriptor, e.g. from fs.openSync() */
752752
return pipe;
753753
}
754754

0 commit comments

Comments
 (0)