use std::sync::atomic::{AtomicI32, AtomicU64, AtomicUsize, Ordering};
const SLOTS: usize = 64;
static PIDS: [AtomicI32; SLOTS] = [const { AtomicI32::new(0) }; SLOTS];
static STATUSES: [AtomicI32; SLOTS] = [const { AtomicI32::new(0) }; SLOTS];
static UTIMES: [AtomicU64; SLOTS] = [const { AtomicU64::new(0) }; SLOTS];
static STIMES: [AtomicU64; SLOTS] = [const { AtomicU64::new(0) }; SLOTS];
static CURSOR: AtomicUsize = AtomicUsize::new(0);
#[cfg(unix)]
pub fn record(pid: i32, status: i32) {
record_rusage(pid, status, 0, 0);
}
#[cfg(unix)]
pub fn record_rusage(pid: i32, status: i32, utime_us: u64, stime_us: u64) {
if pid <= 0 || !(libc::WIFEXITED(status) || libc::WIFSIGNALED(status)) {
return;
}
let slot = CURSOR.fetch_add(1, Ordering::SeqCst) % SLOTS;
PIDS[slot].store(0, Ordering::SeqCst);
STATUSES[slot].store(status, Ordering::SeqCst);
UTIMES[slot].store(utime_us, Ordering::SeqCst);
STIMES[slot].store(stime_us, Ordering::SeqCst);
PIDS[slot].store(pid, Ordering::SeqCst);
}
#[cfg(unix)]
pub fn take(pid: i32) -> Option<i32> {
take_rusage(pid).map(|(status, _, _)| status)
}
#[cfg(unix)]
pub fn take_rusage(pid: i32) -> Option<(i32, u64, u64)> {
if pid <= 0 {
return None;
}
for slot in 0..SLOTS {
if PIDS[slot].load(Ordering::SeqCst) != pid {
continue;
}
let status = STATUSES[slot].load(Ordering::SeqCst);
let utime = UTIMES[slot].load(Ordering::SeqCst);
let stime = STIMES[slot].load(Ordering::SeqCst);
if PIDS[slot]
.compare_exchange(pid, 0, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
{
return Some((status, utime, stime));
}
}
None
}
#[cfg(test)]
mod tests {
#[test]
fn a_recorded_status_is_claimable_exactly_once() {
super::record(999_001, 0x0500);
assert_eq!(super::take(999_001), Some(0x0500));
assert_eq!(super::take(999_001), None);
}
#[test]
fn a_stop_status_is_not_recorded() {
super::record(999_002, 0x117f);
assert_eq!(super::take(999_002), None);
}
}