1# os_pipe.rs [](https://github.com/oconnor663/os_pipe.rs/actions) [](https://crates.io/crates/os_pipe) [](https://docs.rs/os_pipe) 2 3A cross-platform library for opening OS pipes, like those from 4[`pipe`](https://man7.org/linux/man-pages/man2/pipe.2.html) on Linux 5or 6[`CreatePipe`](https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe) 7on Windows. The Rust standard library provides 8[`Stdio::piped`](https://doc.rust-lang.org/std/process/struct.Stdio.html#method.piped) 9for simple use cases involving child processes, but it doesn't 10support creating pipes directly. This crate fills that gap. 11 12- [Docs](https://docs.rs/os_pipe) 13- [Crate](https://crates.io/crates/os_pipe) 14- [Repo](https://github.com/oconnor663/os_pipe.rs) 15 16## Common deadlocks related to pipes 17 18When you work with pipes, you often end up debugging a deadlock at 19some point. These can be confusing if you don't know why they 20happen. Here are two things you need to know: 21 221. Pipe reads will block waiting for input as long as there's at 23 least one writer still open. **If you forget to close a writer, 24 reads will block forever.** This includes writers that you give 25 to child processes. 262. Pipes have an internal buffer of some fixed size. On Linux for 27 example, pipe buffers are 64 KiB by default. When the buffer is 28 full, writes will block waiting for space. **If the buffer is 29 full and there aren't any readers, writes will block forever.** 30 31Deadlocks caused by a forgotten writer usually show up immediately, 32which makes them relatively easy to fix once you know what to look 33for. (See "Avoid a deadlock!" in the example code below.) However, 34deadlocks caused by full pipe buffers are trickier. These might only 35show up for larger inputs, and they might be timing-dependent or 36platform-dependent. If you find that writing to a pipe deadlocks 37sometimes, think about who's supposed to be reading from that pipe, 38and whether that thread or process might be blocked on something 39else. For more on this, see the [Gotchas 40Doc](https://github.com/oconnor663/duct.py/blob/master/gotchas.md#using-io-threads-to-avoid-blocking-children) 41from the [`duct`](https://github.com/oconnor663/duct.rs) crate. (And 42consider whether [`duct`](https://github.com/oconnor663/duct.rs) 43might be a good fit for your use case.) 44 45## Examples 46 47Here we write a single byte into a pipe and read it back out: 48 49```rust 50use std::io::prelude::*; 51 52let (mut reader, mut writer) = os_pipe::pipe()?; 53// XXX: If this write blocks, we'll never get to the read. 54writer.write_all(b"x")?; 55let mut output = [0]; 56reader.read_exact(&mut output)?; 57assert_eq!(b"x", &output); 58``` 59 60This is a minimal working example, but as discussed in the section 61above, reading and writing on the same thread like this is 62deadlock-prone. If we wrote 100 KB instead of just one byte, this 63example would block on `write_all`, it would never make it to 64`read_exact`, and that would be a deadlock. Doing the read and write 65from different threads or different processes would fix the 66deadlock. 67 68For a more complex example, here we join the stdout and stderr of a 69child process into a single pipe. To do that we open a pipe, clone 70its writer, and set that pair of writers as the child's stdout and 71stderr. (This is possible because `PipeWriter` implements 72`Into<Stdio>`.) Then we can read interleaved output from the pipe 73reader. This example is deadlock-free, but note the comment about 74closing the writers. 75 76```rust 77// We're going to spawn a child process that prints "foo" to stdout 78// and "bar" to stderr, and we'll combine these into a single pipe. 79let mut command = std::process::Command::new("python"); 80command.args(&["-c", r#" 81import sys 82sys.stdout.write("foo") 83sys.stdout.flush() 84sys.stderr.write("bar") 85sys.stderr.flush() 86"#]); 87 88// Here's the interesting part. Open a pipe, clone its writer, and 89// set that pair of writers as the child's stdout and stderr. 90let (mut reader, writer) = os_pipe::pipe()?; 91let writer_clone = writer.try_clone()?; 92command.stdout(writer); 93command.stderr(writer_clone); 94 95// Now start the child process running. 96let mut handle = command.spawn()?; 97 98// Avoid a deadlock! This parent process is still holding open pipe 99// writers inside the Command object, and we have to close those 100// before we read. Here we do this by dropping the Command object. 101drop(command); 102 103// Finally we can read all the output and clean up the child. 104let mut output = String::new(); 105reader.read_to_string(&mut output)?; 106handle.wait()?; 107assert_eq!(output, "foobar"); 108``` 109 110Note that the [`duct`](https://github.com/oconnor663/duct.rs) crate 111can reproduce the example above in a single line of code, with no 112risk of deadlocks and no risk of leaking [zombie 113children](https://en.wikipedia.org/wiki/Zombie_process). 114