Skip to main content

stackless_core/
process.rs

1//! PID + process start time: the PID-reuse-safe liveness identity used
2//! for operation locks (§2) and daemon supervision (§3).
3
4use sysinfo::{Pid, ProcessRefreshKind, ProcessesToUpdate, System};
5
6use crate::types::{Pid as StacklessPid, ProcessStartTime};
7
8/// Identifies one incarnation of one process.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct ProcessStamp {
11    pub pid: StacklessPid,
12    /// Unix seconds the process started, per the OS.
13    pub start_time: ProcessStartTime,
14}
15
16impl ProcessStamp {
17    /// The stamp of the calling process.
18    pub fn current() -> Self {
19        let pid = StacklessPid::from_os(std::process::id());
20        Self {
21            pid,
22            start_time: start_time_of(pid).unwrap_or(ProcessStartTime::from_os(0)),
23        }
24    }
25
26    /// The stamp of an arbitrary live process, if it exists.
27    pub fn of(pid: u32) -> Option<Self> {
28        let pid = StacklessPid::from_os(pid);
29        start_time_of(pid).map(|start_time| Self { pid, start_time })
30    }
31
32    /// True only if a process with this PID exists *and* started at the
33    /// recorded time — a recycled PID does not count.
34    pub fn is_alive(&self) -> bool {
35        start_time_of(self.pid).is_some_and(|start| start == self.start_time)
36    }
37}
38
39fn start_time_of(pid: StacklessPid) -> Option<ProcessStartTime> {
40    let raw = pid.get();
41    let mut system = System::new();
42    system.refresh_processes_specifics(
43        ProcessesToUpdate::Some(&[Pid::from_u32(raw)]),
44        false,
45        ProcessRefreshKind::nothing(),
46    );
47    system
48        .process(Pid::from_u32(raw))
49        .map(sysinfo::Process::start_time)
50        .map(ProcessStartTime::from_os)
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn current_process_is_alive() {
59        let stamp = ProcessStamp::current();
60        assert!(stamp.start_time.get() > 0);
61        assert!(stamp.is_alive());
62    }
63
64    #[test]
65    fn wrong_start_time_is_not_alive() {
66        let stamp = ProcessStamp {
67            pid: StacklessPid::from_os(std::process::id()),
68            start_time: ProcessStartTime::from_os(1),
69        };
70        assert!(!stamp.is_alive());
71    }
72
73    #[test]
74    fn bogus_pid_is_not_alive() {
75        let stamp = ProcessStamp {
76            pid: StacklessPid::from_os(u32::MAX - 1),
77            start_time: ProcessStartTime::from_os(1),
78        };
79        assert!(!stamp.is_alive());
80    }
81}