@@ -527,10 +527,25 @@ impl Stdio {
527527 } ,
528528
529529 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).
531544 let pipes = if stdio_id == c:: STD_INPUT_HANDLE {
545+ // For stdin both sides of the pipe are synchronous.
532546 Pipes :: new_synchronous ( false , true ) ?
533547 } else {
548+ // For stdout/stderr our side of the pipe is async and their side is synchronous.
534549 pipe:: anon_pipe ( true , true ) ?
535550 } ;
536551 * pipe = Some ( pipes. ours ) ;
@@ -567,6 +582,9 @@ impl Stdio {
567582
568583impl From < AnonPipe > for Stdio {
569584 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.
570588 match pipe {
571589 AnonPipe :: Sync ( handle) => Stdio :: Handle ( handle) ,
572590 AnonPipe :: Async ( handle) => Stdio :: AsyncPipe ( handle) ,
0 commit comments