8000 Difference in behavior between Stdio::piped() and tokio::net::unix::pipe · Issue #7376 · tokio-rs/tokio · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
Difference in behavior between Stdio::piped() and tokio::net::unix::pipe #7376
Open
@stepancheg

Description

@stepancheg

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

/// If you need to create a pipe for communication with a spawned process, you can
/// use [`Stdio::piped()`] instead.

explain the difference.

Describe alternatives you've considered

None.

Additional context

Reproduces on Mac and Linux.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0