@@ -31,14 +31,14 @@ pub trait Stream: std::io::Read + std::io::Write + Send + Sync {
3131
3232/// Represents a file open in a shell context.
3333///
34- /// File-backed and pipe-backed variants store their handles behind an [`Arc`] so that cloning
35- /// an `OpenFile` (which happens whenever the shell forks off a subshell, command substitution,
36- /// background job, or function call context) merely bumps a reference count instead of issuing
37- /// a `dup(2)` syscall. Because brush runs subshells as in- process tasks (rather than via
38- /// `fork(2)` like a traditional shell), every duplicated descriptor consumes a slot in the
39- /// single process-wide descriptor table; sharing avoids exhausting that table during deeply
40- /// nested or highly concurrent execution. A real duplicate is only materialized when a
41- /// descriptor must be handed to an external child process (see [`OpenFile::try_clone_to_owned`]) .
34+ /// File- and pipe-backed variants hold their handle behind an [`Arc`], so cloning an `OpenFile`
35+ /// shares the underlying descriptor by reference count. The shell opens a fresh context for each
36+ /// subshell, command substitution, background job, and function call and runs them as in-process
37+ /// tasks against one process-wide descriptor table (rather than via `fork(2)` like a traditional
38+ /// shell); sharing the descriptor keeps deeply nested or highly concurrent execution from
39+ /// exhausting that table. A descriptor is duplicated for real only when an independently owned
40+ /// copy is needed to hand to an external child process — see [`OpenFile::try_clone_to_owned`] and
41+ /// the `Stdio` conversion below .
4242pub enum OpenFile {
4343 /// The original standard input this process was started with.
4444 Stdin ( std:: io:: Stdin ) ,
@@ -133,17 +133,6 @@ impl std::fmt::Display for OpenFile {
133133}
134134
135135impl OpenFile {
136- /// Tries to duplicate the open file.
137- ///
138- /// For file- and pipe-backed open files this shares the underlying handle by reference
139- /// count rather than issuing a `dup(2)`; the resulting `OpenFile` refers to the very same
140- /// kernel descriptor (matching the shared open-file-description semantics that `dup` would
141- /// have provided). Use [`OpenFile::try_clone_to_owned`] when an independent descriptor is
142- /// genuinely required (e.g. to hand to a child process).
143- pub fn try_clone ( & self ) -> Result < Self , std:: io:: Error > {
144- Ok ( self . clone ( ) )
145- }
146-
147136 /// Converts the open file into an `OwnedFd`. For shared file/pipe handles this materializes
148137 /// a real duplicate via `dup(2)` so the caller receives an independently owned descriptor.
149138 #[ cfg( unix) ]
@@ -240,54 +229,25 @@ impl From<std::io::PipeWriter> for OpenFile {
240229 }
241230}
242231
243- impl From < OpenFile > for Stdio {
244- fn from ( open_file : OpenFile ) -> Self {
245- // File/pipe handles are shared (behind an `Arc`), so we cannot move the underlying
246- // descriptor out; instead we duplicate it to obtain an owned descriptor for the child.
247- // This is the one place a real `dup` is expected: when wiring up an external process's
248- // standard streams. If the duplication fails (e.g. descriptor exhaustion), fall back to
249- // a null device rather than panicking.
250- fn dup_to_stdio < T : TryCloneToStdio > ( handle : & T ) -> Stdio {
251- handle. try_clone_to_stdio ( ) . unwrap_or_else ( |_| Stdio :: null ( ) )
252- }
232+ impl TryFrom < OpenFile > for Stdio {
233+ type Error = error:: Error ;
253234
235+ fn try_from ( open_file : OpenFile ) -> Result < Self , Self :: Error > {
236+ // File and pipe handles are shared behind an `Arc`, so the descriptor cannot be moved
237+ // out; duplicate it to give the child an independently owned descriptor. Duplication can
238+ // fail (e.g. under descriptor exhaustion), so the conversion is fallible and the error is
239+ // surfaced to the caller rather than silently degrading the child's streams.
254240 match open_file {
255- OpenFile :: Stdin ( _) => Self :: inherit ( ) ,
256- OpenFile :: Stdout ( _) => Self :: inherit ( ) ,
257- OpenFile :: Stderr ( _) => Self :: inherit ( ) ,
258- OpenFile :: File ( f) => dup_to_stdio ( f. as_ref ( ) ) ,
259- OpenFile :: PipeReader ( r) => dup_to_stdio ( r. as_ref ( ) ) ,
260- OpenFile :: PipeWriter ( w) => dup_to_stdio ( w. as_ref ( ) ) ,
261- // NOTE: Custom streams cannot be converted to `Stdio`; we do our best here
262- // and return a null device instead.
263- OpenFile :: Stream ( _) => Self :: null ( ) ,
241+ OpenFile :: Stdin ( _) | OpenFile :: Stdout ( _) | OpenFile :: Stderr ( _) => Ok ( Self :: inherit ( ) ) ,
242+ OpenFile :: File ( f) => Ok ( f. try_clone ( ) ?. into ( ) ) ,
243+ OpenFile :: PipeReader ( r) => Ok ( r. try_clone ( ) ?. into ( ) ) ,
244+ OpenFile :: PipeWriter ( w) => Ok ( w. try_clone ( ) ?. into ( ) ) ,
245+ // Custom streams have no descriptor to hand to a child process.
246+ OpenFile :: Stream ( _) => Ok ( Self :: null ( ) ) ,
264247 }
265248 }
266249}
267250
268- /// Helper trait to duplicate a handle into an owned `Stdio` in a cross-platform way.
269- trait TryCloneToStdio {
270- fn try_clone_to_stdio ( & self ) -> std:: io:: Result < Stdio > ;
271- }
272-
273- impl TryCloneToStdio for std:: fs:: File {
274- fn try_clone_to_stdio ( & self ) -> std:: io:: Result < Stdio > {
275- Ok ( self . try_clone ( ) ?. into ( ) )
276- }
277- }
278-
279- impl TryCloneToStdio for std:: io:: PipeReader {
280- fn try_clone_to_stdio ( & self ) -> std:: io:: Result < Stdio > {
281- Ok ( self . try_clone ( ) ?. into ( ) )
282- }
283- }
284-
285- impl TryCloneToStdio for std:: io:: PipeWriter {
286- fn try_clone_to_stdio ( & self ) -> std:: io:: Result < Stdio > {
287- Ok ( self . try_clone ( ) ?. into ( ) )
288- }
289- }
290-
291251impl std:: io:: Read for OpenFile {
292252 fn read ( & mut self , buf : & mut [ u8 ] ) -> std:: io:: Result < usize > {
293253 match self {
@@ -300,8 +260,8 @@ impl std::io::Read for OpenFile {
300260 ) ) ,
301261 // The handle is shared behind an `Arc`; read through a shared reference (`&File`
302262 // and `&PipeReader` both implement `Read`).
303- Self :: File ( f) => ( & * * f ) . read ( buf) ,
304- Self :: PipeReader ( reader) => ( & * * reader) . read ( buf) ,
263+ Self :: File ( f) => f . as_ref ( ) . read ( buf) ,
264+ Self :: PipeReader ( reader) => reader. as_ref ( ) . read ( buf) ,
305265 Self :: PipeWriter ( _) => Err ( std:: io:: Error :: other (
306266 error:: ErrorKind :: OpenFileNotReadable ( "pipe writer" ) ,
307267 ) ) ,
@@ -320,11 +280,11 @@ impl std::io::Write for OpenFile {
320280 Self :: Stderr ( f) => f. write ( buf) ,
321281 // The handle is shared behind an `Arc`; write through a shared reference (`&File`
322282 // and `&PipeWriter` both implement `Write`).
323- Self :: File ( f) => ( & * * f ) . write ( buf) ,
283+ Self :: File ( f) => f . as_ref ( ) . write ( buf) ,
324284 Self :: PipeReader ( _) => Err ( std:: io:: Error :: other (
325285 error:: ErrorKind :: OpenFileNotWritable ( "pipe reader" ) ,
326286 ) ) ,
327- Self :: PipeWriter ( writer) => ( & * * writer) . write ( buf) ,
287+ Self :: PipeWriter ( writer) => writer. as_ref ( ) . write ( buf) ,
328288 Self :: Stream ( s) => s. write ( buf) ,
329289 }
330290 }
@@ -334,9 +294,9 @@ impl std::io::Write for OpenFile {
334294 Self :: Stdin ( _) => Ok ( ( ) ) ,
335295 Self :: Stdout ( f) => f. flush ( ) ,
336296 Self :: Stderr ( f) => f. flush ( ) ,
337- Self :: File ( f) => ( & * * f ) . flush ( ) ,
297+ Self :: File ( f) => f . as_ref ( ) . flush ( ) ,
338298 Self :: PipeReader ( _) => Ok ( ( ) ) ,
339- Self :: PipeWriter ( writer) => ( & * * writer) . flush ( ) ,
299+ Self :: PipeWriter ( writer) => writer. as_ref ( ) . flush ( ) ,
340300 Self :: Stream ( s) => s. flush ( ) ,
341301 }
342302 }
0 commit comments