use std::ops::ControlFlow;
use std::time::Duration;
use rustix::io::Errno;
use rustix::process::{self, Pid, Signal};
pub async fn force_terminate(pid: Pid, timeout: Duration) -> Result<(), std::io::Error> {
let identity = process_start_time(pid);
let is_original_alive = || match identity {
Some(start_time) => process_start_time(pid) == Some(start_time),
None => is_alive(pid),
};
match process::kill_process(pid, Signal::TERM) {
Ok(()) => {}
Err(Errno::SRCH) => return Ok(()),
Err(errno) => return Err(errno.into()),
}
let exited = crate::os::process::EXIT_BACKOFF
.retry_sync(
|| {
if is_original_alive() {
ControlFlow::Continue(())
} else {
ControlFlow::Break(())
}
},
timeout,
)
.await;
if exited.is_ok() {
return Ok(());
}
if !is_original_alive() {
return Ok(());
}
match process::kill_process(pid, Signal::KILL) {
Ok(()) => {}
Err(Errno::SRCH) => return Ok(()),
Err(errno) => return Err(errno.into()),
}
Ok(())
}
#[must_use]
pub fn is_alive(pid: Pid) -> bool {
!matches!(process::test_kill_process(pid), Err(Errno::SRCH))
}
pub fn process_start_time(pid: Pid) -> Option<u64> {
let pid = sysinfo::Pid::from_u32(pid.as_raw_nonzero().get().unsigned_abs());
let mut system = sysinfo::System::new();
if system.refresh_process(pid) {
system.process(pid).map(sysinfo::Process::start_time)
} else {
None
}
}