From 90130549f4d82e5d269b40542ee21866a8d4dcc2 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Fri, 8 Apr 2022 11:35:29 +0100 Subject: [PATCH] Windows: Use a pipe relay for chaining pipes --- library/std/src/sys/windows/pipe.rs | 43 ++++++++++++++++++++++++++ library/std/src/sys/windows/process.rs | 8 ++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs index df4f1b24eec26..998ab0ca36ea9 100644 --- a/library/std/src/sys/windows/pipe.rs +++ b/library/std/src/sys/windows/pipe.rs @@ -162,6 +162,46 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res } } +/// Takes an asynchronous source pipe and returns a synchronous pipe suitable +/// for sending to a child process. +/// +/// This is achieved by creating a new set of pipes and spawning a thread that +/// relays messages between the source and the synchronous pipe. +pub fn spawn_pipe_relay( + source: &AnonPipe, + ours_readable: bool, + their_handle_inheritable: bool, +) -> io::Result { + // We need this handle to live for the lifetime of the thread spawned below. + let source = source.duplicate()?; + + // create a new pair of anon pipes. + let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?; + + // Spawn a thread that passes messages from one pipe to the other. + // Any errors will simply cause the thread to exit. + let (reader, writer) = if ours_readable { (ours, source) } else { (source, ours) }; + crate::thread::spawn(move || { + let mut buf = [0_u8; 4096]; + 'reader: while let Ok(len) = reader.read(&mut buf) { + if len == 0 { + break; + } + let mut start = 0; + while let Ok(written) = writer.write(&buf[start..len]) { + start += written; + if start == len { + continue 'reader; + } + } + break; + } + }); + + // Return the pipe that should be sent to the child process. + Ok(theirs) +} + fn random_number() -> usize { static N: AtomicUsize = AtomicUsize::new(0); loop { @@ -189,6 +229,9 @@ impl AnonPipe { pub fn into_handle(self) -> Handle { self.inner } + fn duplicate(&self) -> io::Result { + self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner }) + } pub fn read(&self, buf: &mut [u8]) -> io::Result { let result = unsafe { diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index a13585a02224a..a0c0f5dc3ec2c 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -172,6 +172,7 @@ pub enum Stdio { Inherit, Null, MakePipe, + Pipe(AnonPipe), Handle(Handle), } @@ -528,6 +529,11 @@ impl Stdio { Ok(pipes.theirs.into_handle()) } + Stdio::Pipe(ref source) => { + let ours_readable = stdio_id != c::STD_INPUT_HANDLE; + pipe::spawn_pipe_relay(source, ours_readable, true).map(AnonPipe::into_handle) + } + Stdio::Handle(ref handle) => handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS), // Open up a reference to NUL with appropriate read/write @@ -552,7 +558,7 @@ impl Stdio { impl From for Stdio { fn from(pipe: AnonPipe) -> Stdio { - Stdio::Handle(pipe.into_handle()) + Stdio::Pipe(pipe) } }