How do I ensure that a spawned Child process is killed if my app panics?
Asked Answered
N

2

10

I'm writing a small test that starts a daemon process and tests it e.g:

let server = Command::new("target/debug/server").spawn();

// do some tests

server.kill();

The typical way to fail a test is to panic. Unfortunately this means that kill() never gets invoked and repeated runs of the test suite fail, because the port is taken by the old process that is still running.

Is there something like a TRAP function that I can use to ensure the Child gets killed?

Nathalie answered 29/5, 2015 at 19:52 Comment(0)
Z
4

You can put the possibly-panicking code into a closure and give that closure to catch_panic. catch_panic acts the same way a scoped or spawned thread does on joining. It returns a Result with either Ok(ClosureRetVal) or an Err(Box<Any>) if the closure panicked.

let res = std::thread::catch_panic(|| {
    panic!("blub: {}", 35);
});
if let Err(err) = res {
    let msg: String = *err.downcast().unwrap();
    println!("{}", msg);
}

PlayPen

Zephyrus answered 29/5, 2015 at 20:39 Comment(0)
U
9

You can use standard RAII patterns to ensure the child thread is killed if you leave a given scope. If you want to kill your child only if you are panicking, you can insert a check to std::thread::panicking.

use std::process::{Command,Child};

struct ChildGuard(Child);

impl Drop for ChildGuard {
    fn drop(&mut self) {
        // You can check std::thread::panicking() here
        match self.0.kill() {
            Err(e) => println!("Could not kill child process: {}", e),
            Ok(_) => println!("Successfully killed child process"),
        }
    }
}

fn main() {
    let child = Command::new("/bin/cat").spawn().unwrap();
    let _guard = ChildGuard(child);

    panic!("Main thread panicking");
}
Upcountry answered 29/5, 2015 at 22:44 Comment(3)
Are Drop implementations guaranteed to run if the thread panics?Diaphaneity
I think so, unless the panic was started in a destructor (playpen), which is a bad idea in Rust anyway for various reasons. There is some documentation about how the unwinding process happens in std::rt::unwind.Upcountry
Thanks, this is roughly what I ended up doing. Although now since I have it in a Once, there appears to be no way to the Drop trait can be executed.Nathalie
Z
4

You can put the possibly-panicking code into a closure and give that closure to catch_panic. catch_panic acts the same way a scoped or spawned thread does on joining. It returns a Result with either Ok(ClosureRetVal) or an Err(Box<Any>) if the closure panicked.

let res = std::thread::catch_panic(|| {
    panic!("blub: {}", 35);
});
if let Err(err) = res {
    let msg: String = *err.downcast().unwrap();
    println!("{}", msg);
}

PlayPen

Zephyrus answered 29/5, 2015 at 20:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.