• Home
Name Date Size #Lines LOC

..--

src/04-Jul-2025-794458

.android-checksum.jsonD04-Jul-20251.3 KiB11

.cargo-checksum.jsonD04-Jul-2025987 11

Android.bpD04-Jul-20252.2 KiB9689

Cargo.lockD04-Jul-20252.6 KiB9178

Cargo.tomlD04-Jul-20251.3 KiB6455

LICENSED04-Jul-20251 KiB2016

METADATAD04-Jul-2025381 1817

MODULE_LICENSE_MITD04-Jul-20250

README.mdD04-Jul-20254.8 KiB11495

README.tplD04-Jul-2025332 42

cargo_embargo.jsonD04-Jul-2025103 65

README.md

1# os_pipe.rs [![Actions Status](https://github.com/oconnor663/os_pipe.rs/workflows/tests/badge.svg)](https://github.com/oconnor663/os_pipe.rs/actions) [![crates.io](https://img.shields.io/crates/v/os_pipe.svg)](https://crates.io/crates/os_pipe) [![docs.rs](https://docs.rs/os_pipe/badge.svg)](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

README.tpl