I am currently writing an application that also starts other applications (e.g. firefox
). I want these child applications to outlive the parent (e.g. they should continue to run when the parent exits). This is working (see my code below) as long as the parent exits itself (end of main, process:exit()
) but if the parent receives SIGINT
(ctrl + c), SIGTERM
all child processes instantly die with it as well. How can I avoid this? Note: My main process is intended to be long-lived so all the examples below which exit themselves right after spawning the child are not suitable for my case, I just listed them for completeness to show what I've tried, etc.
For now I only care for Linux support if there is no clean cross-plattform solution for this.
So far, I have tried the following methods, none of them working to my satisfaction:
use std::{
process::{self, Child, Command, Stdio},
thread,
};
const EXECUTABLE: &str = "/usr/bin/firefox";
fn main() {
// The child continues to live after our process has finished
spawn_and_exit();
// The child continues to live after our process has cleanly finished
//spawn_and_continue()
// The child gets killed as well if our process gets killed
//spawn_and_force_shutdown()
// Child continues to live (if our process shuts down cleanly)
//threaded_clean_spawn()
// Child gets killed as well
//threaded_and_force_shutdown()
// child gets killed as well
//double_threaded_and_force_shutdown()
}
fn wait() {
std::thread::sleep(std::time::Duration::from_millis(250));
}
fn hang() {
println!("You can now kill the process (e.g. Ctrl+C)");
loop { wait(); }
}
/// The child continues to live after our process has finished
fn spawn_and_exit() {
println!("Spawn and exit");
let _child = Command::new(EXECUTABLE)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
// give the process some time to actually start
wait();
wait();
process::exit(0);
}
/// The child continues to live after our process has finished
fn spawn_and_continue() {
println!("Spawn and clean shutdown");
let _child = Command::new(EXECUTABLE)
//.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
// give the process some time to actually start
wait();
}
/// The child gets killed as well if our process gets killed
fn spawn_and_force_shutdown() {
println!("Spawn and force shutdown");
let _child = Command::new(EXECUTABLE)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
wait();
hang();
}
/// Child continues to live (if our process shuts down cleanly)
fn threaded_clean_spawn() {
println!("threaded_clean_spawn");
let _joinhandle = thread::Builder::new().spawn(|| {
spawn_and_continue();
});
wait();
}
/// Child gets killed as well
fn threaded_and_force_shutdown() {
println!("threaded_and_force_shutdown");
let _joinhandle = thread::Builder::new().spawn(|| {
spawn_and_continue();
});
hang();
}
/// child gets killed as well
fn double_threaded_and_force_shutdown() {
println!("double_threaded_and_force_shutdown");
let _joinhandle = thread::Builder::new().spawn(|| {
let joinhandle = thread::Builder::new().spawn(move || {
spawn_and_continue();
}).unwrap();
let _ = joinhandle.join();
println!("inner thing returned");
});
hang();
}
Side-note: Originally, I expected that thread::Builder::new().spawn()
would solve my problem because the documentation (https://doc.rust-lang.org/std/thread/struct.Builder.html#method.spawn) states:
The spawned thread may outlive the caller (unless the caller thread is the main thread; the whole process is terminated when the main thread finishes).
Due to the addition in the brackets, I also tried the double_threaded_and_force_shutdown
method, without success.
This is basically the same question as How to Spawn Child Processes that Don't Die with Parent? but for Rust instead of c++.