Skip to content

Commit 705d333

Browse files
std: Add Child::terminate as an alternative to Child::kill
Using SIGKILL to stop child processes doesn't always work well, especially if a child process needs to free a file lock. SIGTERM lets child processes cleanup before exiting, and it's usually a better default. Note that the Unix `kill` utility sends SIGTERM by default.
1 parent 95a5c59 commit 705d333

File tree

7 files changed

+81
-19
lines changed

7 files changed

+81
-19
lines changed

library/std/src/process.rs

+30
Original file line numberDiff line numberDiff line change
@@ -2019,6 +2019,36 @@ impl Child {
20192019
self.handle.kill()
20202020
}
20212021

2022+
/// Ask a child process to exit.
2023+
///
2024+
/// On Unix platforms this sends SIGTERM, which can be blocked, handled, or ignored.
2025+
///
2026+
/// On Windows, and other platforms where there isn't a corrollary to SIGTERM, this just
2027+
/// executes `Child::kill`.
2028+
///
2029+
/// # Examples
2030+
///
2031+
/// Basic usage:
2032+
///
2033+
/// ```no_run
2034+
/// #![feature(process_terminate)]
2035+
/// use std::process::Command;
2036+
///
2037+
/// let mut command = Command::new("yes");
2038+
/// if let Ok(mut child) = command.spawn() {
2039+
/// if let Err(e) = child.terminate() {
2040+
/// println!("Child didn't respond to terminate");
2041+
/// child.kill().expect("command couldn't be killed");
2042+
/// }
2043+
/// } else {
2044+
/// println!("yes command didn't start");
2045+
/// }
2046+
/// ```
2047+
#[unstable(feature = "process_terminate", issue = "none")]
2048+
pub fn terminate(&mut self) -> io::Result<()> {
2049+
self.handle.terminate()
2050+
}
2051+
20222052
/// Returns the OS-assigned process identifier associated with this child.
20232053
///
20242054
/// # Examples

library/std/src/process/tests.rs

+21-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::io::prelude::*;
22

3-
use super::{Command, Output, Stdio};
3+
use super::{Child, Command, Output, Stdio};
44
use crate::io::{BorrowedBuf, ErrorKind};
55
use crate::mem::MaybeUninit;
66
use crate::str;
@@ -61,11 +61,13 @@ fn exit_reported_right() {
6161
fn signal_reported_right() {
6262
use crate::os::unix::process::ExitStatusExt;
6363

64-
let mut p = shell_cmd().arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap();
65-
p.kill().unwrap();
66-
match p.wait().unwrap().signal() {
67-
Some(9) => {}
68-
result => panic!("not terminated by signal 9 (instead, {result:?})"),
64+
for (kill, signal) in [Child::kill, Child::terminate].into_iter().zip([9, 15]) {
65+
let mut p = shell_cmd().arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap();
66+
kill(&mut p).unwrap();
67+
match p.wait().unwrap().signal() {
68+
Some(got) if got == signal => {}
69+
result => panic!("not terminated by signal {signal} (instead, {result:?})"),
70+
}
6971
}
7072
}
7173

@@ -715,15 +717,17 @@ fn run_canonical_bat_script() {
715717

716718
#[test]
717719
fn terminate_exited_process() {
718-
let mut cmd = if cfg!(target_os = "android") {
719-
let mut p = shell_cmd();
720-
p.args(&["-c", "true"]);
721-
p
722-
} else {
723-
known_command()
724-
};
725-
let mut p = cmd.stdout(Stdio::null()).spawn().unwrap();
726-
p.wait().unwrap();
727-
assert!(p.kill().is_ok());
728-
assert!(p.kill().is_ok());
720+
for kill in [Child::kill, Child::terminate] {
721+
let mut cmd = if cfg!(target_os = "android") {
722+
let mut p = shell_cmd();
723+
p.args(&["-c", "true"]);
724+
p
725+
} else {
726+
known_command()
727+
};
728+
let mut p = cmd.stdout(Stdio::null()).spawn().unwrap();
729+
p.wait().unwrap();
730+
assert!(kill(&mut p).is_ok());
731+
assert!(kill(&mut p).is_ok());
732+
}
729733
}

library/std/src/sys/unix/process/process_fuchsia.rs

+4
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ impl Process {
165165
Ok(())
166166
}
167167

168+
pub fn terminate(&mut self) -> io::Result<()> {
169+
self.kill()
170+
}
171+
168172
pub fn wait(&mut self) -> io::Result<ExitStatus> {
169173
use crate::sys::process::zircon::*;
170174

library/std/src/sys/unix/process/process_unix.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -812,13 +812,21 @@ impl Process {
812812
}
813813

814814
pub fn kill(&mut self) -> io::Result<()> {
815+
self.send_signal(libc::SIGKILL)
816+
}
817+
818+
pub fn terminate(&mut self) -> io::Result<()> {
819+
self.send_signal(libc::SIGTERM)
820+
}
821+
822+
fn send_signal(&mut self, signal: c_int) -> io::Result<()> {
815823
// If we've already waited on this process then the pid can be recycled
816824
// and used for another process, and we probably shouldn't be killing
817825
// random processes, so return Ok because the process has exited already.
818826
if self.status.is_some() {
819827
Ok(())
820828
} else {
821-
cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
829+
cvt(unsafe { libc::kill(self.pid, signal) }).map(drop)
822830
}
823831
}
824832

library/std/src/sys/unix/process/process_vxworks.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,21 @@ impl Process {
142142
}
143143

144144
pub fn kill(&mut self) -> io::Result<()> {
145+
self.send_signal(libc::SIGKILL)
146+
}
147+
148+
pub fn terminate(&mut self) -> io::Result<()> {
149+
self.send_signal(libc::SIGTERM)
150+
}
151+
152+
fn send_signal(&mut self, signal: c_int) -> io::Result<()> {
145153
// If we've already waited on this process then the pid can be recycled
146154
// and used for another process, and we probably shouldn't be killing
147155
// random processes, so return Ok because the process has exited already.
148156
if self.status.is_some() {
149157
Ok(())
150158
} else {
151-
cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
159+
cvt(unsafe { libc::kill(self.pid, signal) }).map(drop)
152160
}
153161
}
154162

library/std/src/sys/unsupported/process.rs

+4
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ impl Process {
207207
self.0
208208
}
209209

210+
pub fn terminate(&mut self) -> io::Result<()> {
211+
self.0
212+
}
213+
210214
pub fn wait(&mut self) -> io::Result<ExitStatus> {
211215
self.0
212216
}

library/std/src/sys/windows/process.rs

+4
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,10 @@ impl Process {
656656
Ok(())
657657
}
658658

659+
pub fn terminate(&mut self) -> io::Result<()> {
660+
self.kill()
661+
}
662+
659663
pub fn id(&self) -> u32 {
660664
unsafe { c::GetProcessId(self.handle.as_raw_handle()) as u32 }
661665
}

0 commit comments

Comments
 (0)