1use std::io;
2
3#[cfg(unix)]
6pub fn is_transient_spawn_error(error: &io::Error) -> bool {
7 error.raw_os_error() == Some(libc::ETXTBSY)
8}
9
10#[cfg(not(unix))]
11pub fn is_transient_spawn_error(_error: &io::Error) -> bool {
12 false
13}
14
15#[cfg(unix)]
19pub fn lower_process_priority(pid: u32, nice: i32) -> io::Result<()> {
20 if !(0..=19).contains(&nice) {
21 return Err(io::Error::new(
22 io::ErrorKind::InvalidInput,
23 "nice value must be between 0 and 19",
24 ));
25 }
26 if unsafe { libc::setpriority(libc::PRIO_PROCESS, pid, nice) } == 0 {
29 Ok(())
30 } else {
31 Err(io::Error::last_os_error())
32 }
33}
34
35#[cfg(not(unix))]
36pub fn lower_process_priority(_pid: u32, nice: i32) -> io::Result<()> {
37 if !(0..=19).contains(&nice) {
38 return Err(io::Error::new(
39 io::ErrorKind::InvalidInput,
40 "nice value must be between 0 and 19",
41 ));
42 }
43 Ok(())
44}
45
46pub struct ProcessGroupGuard {
50 pid: Option<i32>,
51}
52
53impl ProcessGroupGuard {
54 pub fn new(pid: u32) -> Self {
55 Self {
56 pid: i32::try_from(pid).ok().filter(|pid| *pid > 0),
57 }
58 }
59
60 pub fn disarm(&mut self) {
61 self.pid = None;
62 }
63}
64
65impl Drop for ProcessGroupGuard {
66 fn drop(&mut self) {
67 #[cfg(unix)]
68 if let Some(pid) = self.pid {
69 unsafe {
71 libc::kill(-pid, libc::SIGKILL);
72 }
73 }
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[cfg(unix)]
82 #[test]
83 fn process_group_guard_terminates_owned_child_on_drop() {
84 use std::os::unix::process::{CommandExt, ExitStatusExt};
85 let mut child = std::process::Command::new("sleep")
86 .arg("30")
87 .process_group(0)
88 .spawn()
89 .unwrap();
90 drop(ProcessGroupGuard::new(child.id()));
91 assert_eq!(child.wait().unwrap().signal(), Some(libc::SIGKILL));
92 drop(ProcessGroupGuard::new(0));
93 drop(ProcessGroupGuard::new(u32::MAX));
94 }
95
96 #[test]
97 fn priority_rejects_values_outside_the_portable_nice_range() {
98 assert_eq!(
99 lower_process_priority(u32::MAX, -1).unwrap_err().kind(),
100 io::ErrorKind::InvalidInput
101 );
102 assert_eq!(
103 lower_process_priority(u32::MAX, 20).unwrap_err().kind(),
104 io::ErrorKind::InvalidInput
105 );
106 }
107
108 #[cfg(unix)]
109 #[test]
110 fn spawned_background_process_receives_requested_priority() {
111 let mut child = std::process::Command::new("sleep")
112 .arg("30")
113 .spawn()
114 .unwrap();
115 lower_process_priority(child.id(), 10).unwrap();
116 let nice = unsafe { libc::getpriority(libc::PRIO_PROCESS, child.id()) };
118 let _ = child.kill();
119 let _ = child.wait();
120 assert_eq!(nice, 10);
121 }
122}