8

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
j16r
  • 189
  • 2
  • 7

2 Answers2

7

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");
}
Vaelden
  • 2,089
  • 1
  • 12
  • 14
  • Are Drop implementations guaranteed to run if the thread panics? – Shepmaster May 29 '15 at 23:33
  • 2
    I think so, unless the panic was _started_ in a destructor [(playpen)](http://is.gd/Zswi2w), 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](http://doc.rust-lang.org/std/rt/unwind/). – Vaelden May 30 '15 at 10:28
  • 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. – j16r May 30 '15 at 21:55
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

oli_obk
  • 28,729
  • 6
  • 82
  • 98