Skip to content

Add interrupt function to std::process::Child #97

Closed
@JonathanWoollett-Light

Description

@JonathanWoollett-Light

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions