use crate::{Clock, Duration, Instant};
use libc::{clockid_t, timespec, CLOCK_BOOTTIME, CLOCK_MONOTONIC_RAW};
pub trait PosixClock: Default {
const CLOCK_ID: clockid_t;
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
}
}
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
}
}
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))
}
}
#[derive(Default)]
pub struct BootTimeClock;
impl PosixClock for BootTimeClock {
const CLOCK_ID: clockid_t = CLOCK_BOOTTIME;
}
#[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;
fn generic_posix_clock_test<C: PosixClock>() {
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)
);
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);
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);
assert!(r <= Duration::MILLISECOND);
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>();
}
}