Skip to content

discuss deadlocks in the std::io::pipe() example #141109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 26 additions & 12 deletions library/std/src/io/pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,30 +38,44 @@ use crate::sys_common::{FromInner, IntoInner};
/// > not rely on a particular capacity: an application should be designed so that a reading process
/// > consumes data as soon as it is available, so that a writing process does not remain blocked.
///
/// # Examples
/// # Example
///
/// ```no_run
/// # #[cfg(miri)] fn main() {}
/// # #[cfg(not(miri))]
/// # fn main() -> std::io::Result<()> {
/// use std::io::{Read, Write, pipe};
/// use std::process::Command;
/// use std::io::{pipe, Read, Write};
/// let (ping_rx, mut ping_tx) = pipe()?;
/// let (mut pong_rx, pong_tx) = pipe()?;
/// let (ping_reader, mut ping_writer) = pipe()?;
/// let (mut pong_reader, pong_writer) = pipe()?;
///
/// // Spawn a process that echoes its input.
/// let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?;
/// // Spawn a child process that echoes its input.
/// let mut echo_command = Command::new("cat");
/// echo_command.stdin(ping_reader);
/// echo_command.stdout(pong_writer);
/// let mut echo_child = echo_command.spawn()?;
///
/// ping_tx.write_all(b"hello")?;
/// // Close to unblock echo_server's reader.
/// drop(ping_tx);
/// // Send input to the child process. Note that because we're writing all the input before we
/// // read any output, this could deadlock if the child's input and output pipe buffers both
/// // filled up. Those buffers are usually at least a few KB, so "hello" is fine, but for longer
/// // inputs we'd need to read and write at the same time, e.g. using threads.
/// ping_writer.write_all(b"hello")?;
///
/// // `cat` exits when it reads EOF from stdin, but that can't happen while any ping writer
/// // remains open. We need to drop our ping writer, or read_to_string will deadlock below.
/// drop(ping_writer);
///
/// // The pong reader can't report EOF while any pong writer remains open. Our Command object is
/// // holding a pong writer, and again read_to_string will deadlock if we don't drop it.
/// drop(echo_command);
///
/// let mut buf = String::new();
/// // Block until echo_server's writer is closed.
/// pong_rx.read_to_string(&mut buf)?;
/// // Block until `cat` closes its stdout (a pong writer).
/// pong_reader.read_to_string(&mut buf)?;
/// assert_eq!(&buf, "hello");
///
/// echo_server.wait()?;
/// // At this point we know `cat` has exited, but we still need to wait to clean up the "zombie".
/// echo_child.wait()?;
/// # Ok(())
/// # }
/// ```
Expand Down
Loading