Skip to content

Commit ce2d95c

Browse files
committed
Demonstrate best practice for feeding stdin of a child processes
It's possible to create a deadlock with stdin/stdout I/O on a single thread: * the child process may fill its stdout buffer, and have to wait for the parent process to read it, * but the parent process may be waiting until its stdin write finishes before reading the stdout. Therefore, the parent process should use separate threads for writing and reading.
1 parent 3a5d45f commit ce2d95c

File tree

1 file changed

+18
-7
lines changed

1 file changed

+18
-7
lines changed

library/std/src/process.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,15 @@
7171
//! .spawn()
7272
//! .expect("failed to execute child");
7373
//!
74-
//! {
75-
//! // limited borrow of stdin
76-
//! let stdin = child.stdin.as_mut().expect("failed to get stdin");
74+
//! // If the child process fills its stdout buffer, it may end up
75+
//! // waiting until the parent reads the stdout, and not be able to
76+
//! // read stdin in the meantime, causing a deadlock.
77+
//! // Writing from another thread ensures that stdout is being read
78+
//! // at the same time, avoiding the problem.
79+
//! let mut stdin = child.stdin.take().expect("failed to get stdin");
80+
//! std::thread::spawn(move || {
7781
//! stdin.write_all(b"test").expect("failed to write to stdin");
78-
//! }
82+
//! });
7983
//!
8084
//! let output = child
8185
//! .wait_with_output()
@@ -1145,14 +1149,21 @@ impl Stdio {
11451149
/// .spawn()
11461150
/// .expect("Failed to spawn child process");
11471151
///
1148-
/// {
1149-
/// let stdin = child.stdin.as_mut().expect("Failed to open stdin");
1152+
/// let mut stdin = child.stdin.take().expect("Failed to open stdin");
1153+
/// std::thread::spawn(move || {
11501154
/// stdin.write_all("Hello, world!".as_bytes()).expect("Failed to write to stdin");
1151-
/// }
1155+
/// });
11521156
///
11531157
/// let output = child.wait_with_output().expect("Failed to read stdout");
11541158
/// assert_eq!(String::from_utf8_lossy(&output.stdout), "!dlrow ,olleH");
11551159
/// ```
1160+
///
1161+
/// Writing more than a pipe buffer's worth of input to stdin without also reading
1162+
/// stdout and stderr at the same time may cause a deadlock.
1163+
/// This is an issue when running any program that doesn't guarantee that it reads
1164+
/// its entire stdin before writing more than a pipe buffer's worth of output.
1165+
/// The size of a pipe buffer varies on different targets.
1166+
///
11561167
#[stable(feature = "process", since = "1.0.0")]
11571168
pub fn piped() -> Stdio {
11581169
Stdio(imp::Stdio::MakePipe)

0 commit comments

Comments
 (0)