1- //! Module for anonymous pipe
1+ //! A cross-platform anonymous pipe.
22//!
3- //! ```
4- //! #![feature(anonymous_pipe)]
3+ //! This module provides support for anonymous OS pipes, like [pipe] on Linux or [CreatePipe] on
4+ //! Windows.
5+ //!
6+ //! # Behavior
7+ //!
8+ //! A pipe is a synchronous, unidirectional data channel between two or more processes, like an
9+ //! interprocess [`mpsc`](crate::sync::mpsc) provided by the OS. In particular:
10+ //!
11+ //! * A read on a [`PipeReader`] blocks until the pipe is non-empty.
12+ //! * A write on a [`PipeWriter`] blocks when the pipe is full.
13+ //! * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`]
14+ //! returns EOF.
15+ //! * [`PipeReader`] can be shared, but only one process will consume the data in the pipe.
16+ //!
17+ //! # Capacity
18+ //!
19+ //! Pipe capacity is platform dependent. To quote the Linux [man page]:
20+ //!
21+ //! > Different implementations have different limits for the pipe capacity. Applications should
22+ //! > not rely on a particular capacity: an application should be designed so that a reading process
23+ //! > consumes data as soon as it is available, so that a writing process does not remain blocked.
524//!
25+ //! # Examples
26+ //!
27+ //! ```no_run
28+ //! #![feature(anonymous_pipe)]
629//! # #[cfg(miri)] fn main() {}
730//! # #[cfg(not(miri))]
831//! # fn main() -> std::io::Result<()> {
9- //! let (reader, writer) = std::pipe::pipe()?;
32+ //! # use std::process::Command;
33+ //! # use std::io::{Read, Write};
34+ //! let (ping_rx, mut ping_tx) = std::pipe::pipe()?;
35+ //! let (mut pong_rx, pong_tx) = std::pipe::pipe()?;
36+ //!
37+ //! // Spawn a process that echoes its input.
38+ //! let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?;
39+ //!
40+ //! ping_tx.write_all(b"hello")?;
41+ //! // Close to unblock echo_server's reader.
42+ //! drop(ping_tx);
43+ //!
44+ //! let mut buf = String::new();
45+ //! // Block until echo_server's writer is closed.
46+ //! pong_rx.read_to_string(&mut buf)?;
47+ //! assert_eq!(&buf, "hello");
48+ //!
49+ //! echo_server.wait()?;
1050//! # Ok(())
1151//! # }
1252//! ```
13-
53+ //! [pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html
54+ //! [CreatePipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
55+ //! [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html
1456use crate :: io;
1557use crate :: sys:: anonymous_pipe:: { AnonPipe , pipe as pipe_inner} ;
1658
1759/// Create anonymous pipe that is close-on-exec and blocking.
60+ ///
61+ /// # Examples
62+ ///
63+ /// See the [module-level](crate::pipe) documentation for examples.
1864#[ unstable( feature = "anonymous_pipe" , issue = "127154" ) ]
1965#[ inline]
2066pub fn pipe ( ) -> io:: Result < ( PipeReader , PipeWriter ) > {
@@ -33,6 +79,58 @@ pub struct PipeWriter(pub(crate) AnonPipe);
3379
3480impl PipeReader {
3581 /// Create a new [`PipeReader`] instance that shares the same underlying file description.
82+ ///
83+ /// # Examples
84+ ///
85+ /// ```no_run
86+ /// #![feature(anonymous_pipe)]
87+ /// # #[cfg(miri)] fn main() {}
88+ /// # #[cfg(not(miri))]
89+ /// # fn main() -> std::io::Result<()> {
90+ /// # use std::fs;
91+ /// # use std::io::Write;
92+ /// # use std::process::Command;
93+ /// const NUM_SLOT: u8 = 2;
94+ /// const NUM_PROC: u8 = 5;
95+ /// const OUTPUT: &str = "work.txt";
96+ ///
97+ /// let mut jobs = vec![];
98+ /// let (reader, mut writer) = std::pipe::pipe()?;
99+ ///
100+ /// // Write NUM_SLOT characters the the pipe.
101+ /// writer.write_all(&[b'|'; NUM_SLOT as usize])?;
102+ ///
103+ /// // Spawn several processes that read a character from the pipe, do some work, then
104+ /// // write back to the pipe. When the pipe is empty, the processes block, so only
105+ /// // NUM_SLOT processes can be working at any given time.
106+ /// for _ in 0..NUM_PROC {
107+ /// jobs.push(
108+ /// Command::new("bash")
109+ /// .args(["-c",
110+ /// &format!(
111+ /// "read -n 1\n\
112+ /// echo -n 'x' >> '{OUTPUT}'\n\
113+ /// echo -n '|'",
114+ /// ),
115+ /// ])
116+ /// .stdin(reader.try_clone()?)
117+ /// .stdout(writer.try_clone()?)
118+ /// .spawn()?,
119+ /// );
120+ /// }
121+ ///
122+ /// // Wait for all jobs to finish.
123+ /// for mut job in jobs {
124+ /// job.wait()?;
125+ /// }
126+ ///
127+ /// // Check our work and clean up.
128+ /// let xs = fs::read_to_string(OUTPUT)?;
129+ /// fs::remove_file(OUTPUT)?;
130+ /// assert_eq!(xs, "x".repeat(NUM_PROC.into()));
131+ /// # Ok(())
132+ /// # }
133+ /// ```
36134 #[ unstable( feature = "anonymous_pipe" , issue = "127154" ) ]
37135 pub fn try_clone ( & self ) -> io:: Result < Self > {
38136 self . 0 . try_clone ( ) . map ( Self )
@@ -41,6 +139,38 @@ impl PipeReader {
41139
42140impl PipeWriter {
43141 /// Create a new [`PipeWriter`] instance that shares the same underlying file description.
142+ ///
143+ /// # Examples
144+ ///
145+ /// ```no_run
146+ /// #![feature(anonymous_pipe)]
147+ /// # #[cfg(miri)] fn main() {}
148+ /// # #[cfg(not(miri))]
149+ /// # fn main() -> std::io::Result<()> {
150+ /// # use std::process::Command;
151+ /// # use std::io::Read;
152+ /// let (mut reader, writer) = std::pipe::pipe()?;
153+ ///
154+ /// // Spawn a process that writes to stdout and stderr.
155+ /// let mut peer = Command::new("bash")
156+ /// .args([
157+ /// "-c",
158+ /// "echo -n foo\n\
159+ /// echo -n bar >&2"
160+ /// ])
161+ /// .stdout(writer.try_clone()?)
162+ /// .stderr(writer)
163+ /// .spawn()?;
164+ ///
165+ /// // Read and check the result.
166+ /// let mut msg = String::new();
167+ /// reader.read_to_string(&mut msg)?;
168+ /// assert_eq!(&msg, "foobar");
169+ ///
170+ /// peer.wait()?;
171+ /// # Ok(())
172+ /// # }
173+ /// ```
44174 #[ unstable( feature = "anonymous_pipe" , issue = "127154" ) ]
45175 pub fn try_clone ( & self ) -> io:: Result < Self > {
46176 self . 0 . try_clone ( ) . map ( Self )
0 commit comments