I'm running Commands in a threadpool::ThreadPool, and I need to apply a timeout to each command (i.e., I don't want them running longer that x number of minutes).
I've tried using the try_wait function on a Child in a loop, things seem to get hung up and never finish.
Unfortunately I trashed it, but it went something like this:
let exe_path = // ... a PathBuf ..
let mut command = Command::new(&exe_path);
// ... set up the command's arguments and working directory
let mut child = command.spawn().unwrap();
loop {
match child.try_wait() {
Ok(Some(status)) => {
// gather the stdout and stderr into an Output struct
break;
}
Ok(None) => {
// if it's been too long since we spawned the child process kill it and break
}
Err(e) => {
child.kill().expect("unable to kill process");
break;
}
}
}
I also tried to put child.wait() at the beginning of the Ok(Some(status)) but that didn't help either (the documentation mentions that it's necessary on some systems for releasing resources)...
Here's the rough idea of the solution I ended up with:
enum Msg {
Start(u32),
End(std::io::Result<std::process::Output>),
}
let (tx, rx) = crossbeam::channel::unbounded();
let proc = std::thread::spawn(move || {
// I'll show this to the user in case something bad
// happens and they need to kill the process manually
let _ = tx.send(Msg::Start(std::process::id());
// create and run command
// wait on the output
let _ = tx.send(Msg::End(Ok(output)));
});
let mut pid: u32 = 0;
loop {
match rx.try_recv() {
Ok(Msg::Start(id)) => { pid = id; }
Ok(Msg::End(output)) => {
// handle output
}
Err(e) => {
match e {
crossbeam::TryRecvError::Empty => {
// check if we've spent too much time waiting and exit if so
}
_ => {
// the channel got disconnected - exit
}
}
}
}
}