1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Linux-specific clock implementations
//!
//! These clock implementations are only available on Linux, but they provide
//! additional guarantees with respect to the standard library's implementation.

use crate::{Clock, Duration, Instant};
use libc::{clockid_t, timespec, CLOCK_BOOTTIME, CLOCK_MONOTONIC_RAW};

/// Functionality shared by all POSIX-compliant high-resolution clocks
pub trait PosixClock: Default {
    /// Which clock are we talking about?
    const CLOCK_ID: clockid_t;

    /// Read the current value of this clock, in POSIX format
    fn now_raw() -> timespec {
        unsafe {
            let mut t: timespec = std::mem::uninitialized();
            let res = libc::clock_gettime(Self::CLOCK_ID, &mut t);
            assert_eq!(
                res,
                0,
                "Failed to read Linux clock with id {}",
                Self::CLOCK_ID
            );
            t
        }
    }

    /// Check the resolution (precision) of this clock, in POSIX format
    fn resolution_raw() -> timespec {
        unsafe {
            let mut t: timespec = std::mem::uninitialized();
            let res = libc::clock_getres(Self::CLOCK_ID, &mut t);
            assert_eq!(
                res,
                0,
                "Failed to check resolution of Linux clock with id {}",
                Self::CLOCK_ID
            );
            t
        }
    }

    /// Check the resolution (precision) of this clock, in this crate's format
    fn resolution() -> Duration {
        let t = Self::resolution_raw();
        Duration((t.tv_sec as i64) * 1_000_000_000 + (t.tv_nsec as i64))
    }
}

impl<C: PosixClock> Clock for C {
    fn now(&self) -> Instant {
        let t = Self::now_raw();
        Instant((t.tv_sec as i64) * 1_000_000_000 + (t.tv_nsec as i64))
    }
}

/// Interface to the CLOCK_BOOTTIME Linux clock
///
/// This clock is monotonic (does not jump back in time), but durations may
/// inflate and deflate during clock calibration events (e.g. NTP
/// synchronizations). The resolution is hardware-dependent, but can be queried
/// using the underlying `PosixClock` trait. Suspend is correctly handled, i.e.
/// the clock will jump forward by the sleep duration.
#[derive(Default)]
pub struct BootTimeClock;

impl PosixClock for BootTimeClock {
    const CLOCK_ID: clockid_t = CLOCK_BOOTTIME;
}

/// Interface to the CLOCK_MONOTONIC_RAW Linux clock
///
/// This clock is monotonic (does not jump back in time) and will never be
/// recalibrated, so its durations will be more reliable than those of
/// BootTimeClock on short time scales. The resolution is hardware-dependent,
/// but can be queried using the underlying `PosixClock` trait. Suspend is NOT
/// correctly handled, it's as if no time was spent.
#[derive(Default)]
pub struct MonotonicRawClock;

impl PosixClock for MonotonicRawClock {
    const CLOCK_ID: clockid_t = CLOCK_MONOTONIC_RAW;
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::clocks;

    /// Generic test for a PosixClock
    fn generic_posix_clock_test<C: PosixClock>() {
        // Raw time should go forward
        let t_raw_1 = C::now_raw();
        let t_raw_2 = C::now_raw();
        assert!(
            t_raw_2.tv_sec > t_raw_1.tv_sec
                || (t_raw_2.tv_sec == t_raw_1.tv_sec && t_raw_2.tv_nsec >= t_raw_1.tv_nsec)
        );

        // Raw resolution should not vary over time
        let r_raw_1 = C::resolution_raw();
        let r_raw_2 = C::resolution_raw();
        assert_eq!(r_raw_1.tv_sec, r_raw_2.tv_sec);
        assert_eq!(r_raw_1.tv_nsec, r_raw_2.tv_nsec);

        // Digested resolution should match raw resolution
        let r = C::resolution();
        assert_eq!(r.as_nanos() / 1_000_000_000, r_raw_2.tv_sec as i64);
        assert_eq!(r.as_nanos() % 1_000_000_000, r_raw_2.tv_nsec as i64);

        // Advertised resolution should be at least nanosecond
        assert!(r <= Duration::MILLISECOND);

        // ...and finally we can run the generic clock test
        clocks::tests::generic_clock_test::<C>();
    }

    #[test]
    fn boot_time_clock() {
        generic_posix_clock_test::<BootTimeClock>();
    }

    #[test]
    fn monotonic_raw_clock() {
        generic_posix_clock_test::<MonotonicRawClock>();
    }
}