about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
authorChris Denton <christophersdenton@gmail.com>2022-04-08 11:35:29 +0100
committerChris Denton <christophersdenton@gmail.com>2022-04-08 11:35:29 +0100
commit90130549f4d82e5d269b40542ee21866a8d4dcc2 (patch)
tree74807104e7662e65ca2d3b0258531852398170dc /library/std/src
parente745b4ddbd05026c75aae4506aef39fdfe1603c5 (diff)
downloadrust-90130549f4d82e5d269b40542ee21866a8d4dcc2.tar.gz
rust-90130549f4d82e5d269b40542ee21866a8d4dcc2.zip
Windows: Use a pipe relay for chaining pipes
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/sys/windows/pipe.rs43
-rw-r--r--library/std/src/sys/windows/process.rs8
2 files changed, 50 insertions, 1 deletions
diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs
index df4f1b24eec..998ab0ca36e 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<AnonPipe> {
+    // 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> {
+        self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner })
+    }
 
     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
         let result = unsafe {
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index a13585a0222..a0c0f5dc3ec 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<AnonPipe> for Stdio {
     fn from(pipe: AnonPipe) -> Stdio {
-        Stdio::Handle(pipe.into_handle())
+        Stdio::Pipe(pipe)
     }
 }