Open
Description
Is your feature request related to a problem? Please describe.
Repro code does this:
- create a process
sh -c 'sleep 1000000000'
(exec sleep 1
reproduces it too) - pipes stdout
- kills the process (but does not kill child
sleep
process) - waits for process
- waits for pipe
When using Stdio::piped()
everything works fine.
When using tokio::net::unix::pipe
, last step hangs.
I don't have explanation why it hangs.
#[tokio::test]
async fn repro_hanging_pipe() {
let mut command = Command::new("sh");
command.args(["-c", "sleep 10000000000"]);
// Also reproduces with `sleep 1` and even `exec sleep 1`.
command.stdin(Stdio::null());
command.stderr(Stdio::inherit());
let (mut child, mut stdout_rx): (_, Pin<Box<dyn AsyncRead>>) = if false {
// Works with regular stdout pipe.
command.stdout(Stdio::piped());
let mut child = command.spawn().unwrap();
let stdout_rx = mem::take(&mut child.stdout).unwrap();
(child, Box::pin(stdout_rx))
} else {
// Hangs with tokio::net::unix::pipe.
let (stdout_tx, stdout_rx) = tokio::net::unix::pipe::pipe().unwrap();
command.stdout(stdout_tx.into_blocking_fd().unwrap());
let child = command.spawn().unwrap();
(child, Box::pin(stdout_rx))
};
let mut stdout = Vec::new();
let stdout_fut = stdout_rx.read_to_end(&mut stdout);
eprintln!("Sending SIGKILL");
child.start_kill().unwrap();
// To be safe.
mem::take(&mut child.stdout);
mem::take(&mut child.stderr);
mem::take(&mut child.stdin);
eprintln!("Calling wait");
child.wait().await.unwrap();
eprintln!("Waited; waiting for stdout");
stdout_fut.await.unwrap();
eprintln!("all good; this code is unreachable with tokio::net::unix::pipe");
}
Describe the solution you'd like
Around this line
tokio/tokio/src/net/unix/pipe.rs
Lines 24 to 25 in ab8d7b8
explain the difference.
Describe alternatives you've considered
None.
Additional context
Reproduces on Mac and Linux.