Issue
I have a Rust process that is supposed to launch a subprocess and then immediately exit. This appears to work:
fn main() {
// Intentionally drop the returned Child and exit
Command::new("bash").args(&["-c", "sleep 10s; touch done"]).spawn().unwrap();
}
Running this process exits immediately and the bash
process continues:
$ cargo build; target/debug/demo
$ ps aux | grep bash
dimo414 35959 0.0 0.0 4278616 1484 s001 S 1:12PM 0:00.00 bash -c sleep 10s; touch done
...
However if I add one more layer and try to invoke my binary and await its completion that also appears to wait for the subprocess, unlike what I observe in the shell. Here's an MCVE:
fn main() {
let exec = std::env::current_exe().expect("Could not resolve executable location");
// First re-invoke the same binary and await it
if std::env::args().len() < 2 {
println!("Ran Subprocess:\n{:?}", Command::new(exec).arg("").output().unwrap());
} else {
// In that subprocess spawn a long-running process but don't wait
println!("Spawning Subprocess");
Command::new("bash").args(&["-c", "sleep 10s; touch done"]).spawn().unwrap();
}
}
$ cargo build; target/debug/demo
# doesn't terminate until the bash process does
Is there a way to allow the top-level process to complete without waiting for the nested process?
Solution
Please check the reference of spawn
Executes the command as a child process, returning a handle to it.
By default, stdin, stdout and stderr are inherited from the parent.
When you run self as a subprocess it spawns another subprocess with inheriting stdio from parent. Since your top-level process uses output()
it will wait subprocess to finish and collect its all output (reference).
Let's demonstrate it like this:
Root -> Sub1 -> Sub2
Sub2 uses stdout channel of Sub1, Root waits to collect all output from Sub1 which is still in use by Sub2, at the end of the day Root waits for Sub2 to finish.
Solution is; simply use Stdio::null()
to send output to /dev/null
since your Root process does not care about the output of Sub2.
fn main() {
let exec = std::env::current_exe().expect("Could not resolve executable location");
if std::env::args().len() < 2 {
println!(
"Ran Subprocess:\n{:?}",
Command::new(exec).arg("").output().unwrap()
);
} else {
println!("Spawning Subprocess");
Command::new("bash")
.stdout(Stdio::null())
.stderr(Stdio::null())
.args(&["-c", "sleep 10s; touch done"])
.spawn()
.unwrap();
}
}
Answered By - Ömer Erden Answer Checked By - Marilyn (WPSolving Volunteer)