mod private
{
use std ::io;
use std ::path ::Path;
use core ::time ::Duration;
use std ::time ::Instant;
#[ cfg( unix ) ]
#[ allow( unsafe_code ) ]
pub fn is_process_alive( pid : i32 ) -> io ::Result< bool >
{
if pid <= 0
{
return Err( io ::Error ::new
(
io ::ErrorKind ::InvalidInput,
format!( "PID must be positive, got {pid}" ),
));
}
let result = unsafe { libc ::kill( pid, 0 ) };
if result == 0
{
Ok( true )
}
else
{
let err = io ::Error ::last_os_error();
match err.raw_os_error()
{
Some( libc ::ESRCH ) => Ok( false ),
Some( libc ::EPERM ) => Ok( true ),
_ => Err( err ),
}
}
}
#[ cfg( not( unix ) ) ]
pub fn is_process_alive( _pid : i32 ) -> io ::Result< bool >
{
Err( io ::Error ::new
(
io ::ErrorKind ::Unsupported,
"is_process_alive requires Unix (libc::kill)",
))
}
#[ must_use = "returns `Err` on timeout — ignoring may hide alive processes" ]
pub fn wait_for_exit( pid : i32, timeout : Duration ) -> io ::Result< () >
{
let start = Instant ::now();
let poll_interval = Duration ::from_millis( 50 );
loop
{
if !is_process_alive( pid )?
{
return Ok( () );
}
if start.elapsed() >= timeout
{
return Err( io ::Error ::new
(
io ::ErrorKind ::TimedOut,
format!( "process {pid} still alive after {timeout:?}" ),
));
}
std ::thread ::sleep( poll_interval );
}
}
pub fn is_pidfile_alive( path : &Path ) -> io ::Result< bool >
{
let content = std ::fs ::read_to_string( path )?;
let pid : i32 = content.trim().parse().map_err( | e |
{
io ::Error ::new( io ::ErrorKind ::InvalidData, format!( "invalid PID in file: {e}" ) )
})?;
is_process_alive( pid )
}
}
crate ::mod_interface!
{
own use is_process_alive;
own use wait_for_exit;
own use is_pidfile_alive;
}