@@ -527,10 +527,25 @@ impl Stdio {
527
527
} ,
528
528
529
529
Stdio :: MakePipe => {
530
- // If stdin then make synchronous
530
+ // Handles that are passed to a child process must be synchronous
531
+ // because they will be read synchronously (see #95759).
532
+ // Therefore we prefer to make both ends of a pipe synchronous
533
+ // just in case our end of the pipe is passed to another process.
534
+ //
535
+ // However, we may need to read from both the child's stdout and
536
+ // stderr simultaneously when waiting for output. This requires
537
+ // async reads so as to avoid blocking either pipe.
538
+ //
539
+ // The solution used here is to make handles synchronous
540
+ // except for our side of the stdout and sterr pipes.
541
+ // If our side of those pipes do end up being given to another
542
+ // process then we use a "pipe relay" to synchronize access
543
+ // (see `Stdio::AsyncPipe` below).
531
544
let pipes = if stdio_id == c:: STD_INPUT_HANDLE {
545
+ // For stdin both sides of the pipe are synchronous.
532
546
Pipes :: new_synchronous ( false , true ) ?
533
547
} else {
548
+ // For stdout/stderr our side of the pipe is async and their side is synchronous.
534
549
pipe:: anon_pipe ( true , true ) ?
535
550
} ;
536
551
* pipe = Some ( pipes. ours ) ;
@@ -567,6 +582,9 @@ impl Stdio {
567
582
568
583
impl From < AnonPipe > for Stdio {
569
584
fn from ( pipe : AnonPipe ) -> Stdio {
585
+ // Note that it's very important we don't give async handles to child processes.
586
+ // Therefore if the pipe is asynchronous we must have a way to turn it synchronous.
587
+ // See #95759.
570
588
match pipe {
571
589
AnonPipe :: Sync ( handle) => Stdio :: Handle ( handle) ,
572
590
AnonPipe :: Async ( handle) => Stdio :: AsyncPipe ( handle) ,
0 commit comments