Description
Proposal
Problem statement
With running a binary it is common their will be artefacts which should be removed on ending execution (e.g. a unix socket). When developing such a binary, integration tests will need to run the binary and then clean-up afterwards.
At the moment it is not clear how to do this, this should be a convenient and clear.
Motivation, use-cases
At the moment you might do this like:
let child = std::process::Command("/path/to/my/binary").spawn().unwrap();
unsafe {
libc::kill(child.id() as i32, libc::SIGINT);
}
let exit = child.wait().unwrap();
assert_eq!(exit,Some(0));
Yet if you are in the rarer circumstance where you don't trust the running application and/or need to short-circuit it, it is significantly easier to do:
let child = std::process::Command("/path/to/my/binary").spawn().unwrap();
child.kill().unwrap();
let exit = child.wait().unwrap();
assert_eq!(exit,Some(0));
I would forward that really SIGINT
should be the more common use case than SIGKILL
so it is odd that we have a convenience method for SIGKILL
but not SIGINT
.
Solution sketches
The simple solution would be to add an interrupt
function to std::process::Child
that behaves simiarlly to the kill
function except sends the SIGINT
signal.
let child = std::process::Command("/path/to/my/binary").spawn().unwrap();
child.interrupt().unwrap();
let exit = child.wait().unwrap();
assert_eq!(exit,Some(0));
Notably in the above example it is not guaranteed that the program will exit, especially if it captures the signal then hangs, this may lead to code where you want to interrupt the process then after a given timeout kill the process:
let child = std::process::Command("/path/to/my/binary").spawn().unwrap();
child.interrupt().unwrap();
let now = std::time::Instant::now();
let exit_status = loop {
if now.elapsed() >= std::time::Duration::from_millis(500) {
break None;
}
match child.try_wait() {
Ok(Some(status)) => break Some(status),
Ok(None) => {},
Err(err) => panic!(err)
}
}.unwrap_or_else(|| {
child.kill().unwrap();
child.wait().unwrap()
});
assert_eq!(exit,Some(0));
This case may warrant a interrupt_timeout
function, such that this code could look like:
let child = std::process::Command("/path/to/my/binary").spawn().unwrap();
child.interrupt_timeout(std::time::Duration::from_millis(500)).unwrap();
let exit_status = child.wait().unwrap();
assert_eq!(exit,Some(0));
Links and related work
I've already made a partial PR on this: rust-lang/rust#101387
What happens now?
This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.