sched_clock/clocks/
linux.rs

1//! Linux-specific clock implementations
2//!
3//! These clock implementations are only available on Linux, but they provide
4//! additional guarantees with respect to the standard library's implementation.
5
6use crate::{Clock, Duration, Instant};
7use libc::{clockid_t, timespec, CLOCK_BOOTTIME, CLOCK_MONOTONIC_RAW};
8
9/// Functionality shared by all POSIX-compliant high-resolution clocks
10pub trait PosixClock: Default {
11    /// Which clock are we talking about?
12    const CLOCK_ID: clockid_t;
13
14    /// Read the current value of this clock, in POSIX format
15    fn now_raw() -> timespec {
16        unsafe {
17            let mut t: timespec = std::mem::uninitialized();
18            let res = libc::clock_gettime(Self::CLOCK_ID, &mut t);
19            assert_eq!(
20                res,
21                0,
22                "Failed to read Linux clock with id {}",
23                Self::CLOCK_ID
24            );
25            t
26        }
27    }
28
29    /// Check the resolution (precision) of this clock, in POSIX format
30    fn resolution_raw() -> timespec {
31        unsafe {
32            let mut t: timespec = std::mem::uninitialized();
33            let res = libc::clock_getres(Self::CLOCK_ID, &mut t);
34            assert_eq!(
35                res,
36                0,
37                "Failed to check resolution of Linux clock with id {}",
38                Self::CLOCK_ID
39            );
40            t
41        }
42    }
43
44    /// Check the resolution (precision) of this clock, in this crate's format
45    fn resolution() -> Duration {
46        let t = Self::resolution_raw();
47        Duration((t.tv_sec as i64) * 1_000_000_000 + (t.tv_nsec as i64))
48    }
49}
50
51impl<C: PosixClock> Clock for C {
52    fn now(&self) -> Instant {
53        let t = Self::now_raw();
54        Instant((t.tv_sec as i64) * 1_000_000_000 + (t.tv_nsec as i64))
55    }
56}
57
58/// Interface to the CLOCK_BOOTTIME Linux clock
59///
60/// This clock is monotonic (does not jump back in time), but durations may
61/// inflate and deflate during clock calibration events (e.g. NTP
62/// synchronizations). The resolution is hardware-dependent, but can be queried
63/// using the underlying `PosixClock` trait. Suspend is correctly handled, i.e.
64/// the clock will jump forward by the sleep duration.
65#[derive(Default)]
66pub struct BootTimeClock;
67
68impl PosixClock for BootTimeClock {
69    const CLOCK_ID: clockid_t = CLOCK_BOOTTIME;
70}
71
72/// Interface to the CLOCK_MONOTONIC_RAW Linux clock
73///
74/// This clock is monotonic (does not jump back in time) and will never be
75/// recalibrated, so its durations will be more reliable than those of
76/// BootTimeClock on short time scales. The resolution is hardware-dependent,
77/// but can be queried using the underlying `PosixClock` trait. Suspend is NOT
78/// correctly handled, it's as if no time was spent.
79#[derive(Default)]
80pub struct MonotonicRawClock;
81
82impl PosixClock for MonotonicRawClock {
83    const CLOCK_ID: clockid_t = CLOCK_MONOTONIC_RAW;
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate::clocks;
90
91    /// Generic test for a PosixClock
92    fn generic_posix_clock_test<C: PosixClock>() {
93        // Raw time should go forward
94        let t_raw_1 = C::now_raw();
95        let t_raw_2 = C::now_raw();
96        assert!(
97            t_raw_2.tv_sec > t_raw_1.tv_sec
98                || (t_raw_2.tv_sec == t_raw_1.tv_sec && t_raw_2.tv_nsec >= t_raw_1.tv_nsec)
99        );
100
101        // Raw resolution should not vary over time
102        let r_raw_1 = C::resolution_raw();
103        let r_raw_2 = C::resolution_raw();
104        assert_eq!(r_raw_1.tv_sec, r_raw_2.tv_sec);
105        assert_eq!(r_raw_1.tv_nsec, r_raw_2.tv_nsec);
106
107        // Digested resolution should match raw resolution
108        let r = C::resolution();
109        assert_eq!(r.as_nanos() / 1_000_000_000, r_raw_2.tv_sec as i64);
110        assert_eq!(r.as_nanos() % 1_000_000_000, r_raw_2.tv_nsec as i64);
111
112        // Advertised resolution should be at least nanosecond
113        assert!(r <= Duration::MILLISECOND);
114
115        // ...and finally we can run the generic clock test
116        clocks::tests::generic_clock_test::<C>();
117    }
118
119    #[test]
120    fn boot_time_clock() {
121        generic_posix_clock_test::<BootTimeClock>();
122    }
123
124    #[test]
125    fn monotonic_raw_clock() {
126        generic_posix_clock_test::<MonotonicRawClock>();
127    }
128}