Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 515
j16r Avatar asked May 29 '15 19:05

j16r


2 Answers

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");
}
like image 153
Vaelden Avatar answered Nov 13 '22 03:11

Vaelden


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

like image 23
oli_obk Avatar answered Nov 13 '22 02:11

oli_obk