use crate::daemon::time::{Instant, inner::Clock, instant::Utc};
use nix::time::{ClockId, clock_gettime};
pub struct RealTime;
impl Clock<Utc> for RealTime {
fn get_time(&self) -> Instant {
let now = clock_gettime(ClockId::CLOCK_REALTIME).unwrap();
Instant::from_timespec(now)
}
}
pub struct MonotonicCoarse;
impl Clock<Utc> for MonotonicCoarse {
fn get_time(&self) -> Instant {
#[cfg(not(target_os = "macos"))]
let now = clock_gettime(ClockId::CLOCK_MONOTONIC_COARSE).unwrap();
#[cfg(target_os = "macos")]
let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
Instant::from_timespec(now)
}
}
#[cfg(feature = "daemon")]
mod daemon_clocks {
use super::{Clock, ClockId, Instant, Utc, clock_gettime};
use crate::daemon::{clock_parameters::ClockParameters, io::tsc::ReadTsc, time::TscCount};
pub struct ClockBound<T> {
clock_parameters: ClockParameters,
read_tsc: T,
}
impl<T> ClockBound<T> {
pub fn new(clock_parameters: ClockParameters, read_tsc: T) -> Self {
Self {
clock_parameters,
read_tsc,
}
}
}
impl<T: ReadTsc> Clock<Utc> for ClockBound<T> {
#[allow(clippy::cast_possible_wrap)]
fn get_time(&self) -> Instant {
let current_tsc = TscCount::new(self.read_tsc.read_tsc() as i64);
self.clock_parameters.time
+ ((current_tsc - self.clock_parameters.tsc_count) * self.clock_parameters.period)
}
}
pub struct MonotonicRaw;
impl Clock<Utc> for MonotonicRaw {
fn get_time(&self) -> Instant {
let now = clock_gettime(ClockId::CLOCK_MONOTONIC_RAW).unwrap();
Instant::from_timespec(now)
}
}
}
#[cfg(feature = "daemon")]
pub use daemon_clocks::{ClockBound, MonotonicRaw};
#[cfg(test)]
mod tests {
use rstest::rstest;
use crate::daemon::{
clock_parameters::ClockParameters,
io::tsc::MockReadTsc,
time::{Duration, TscCount, tsc::Period},
};
use super::*;
#[test]
fn test_realtime() {
let realtime = RealTime;
let now = realtime.get_time();
let now_std = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap();
let now = now - Instant::UNIX_EPOCH;
approx::assert_abs_diff_eq!(now_std.as_secs_f64(), now.as_seconds_f64(), epsilon = 0.01)
}
#[cfg(not(target_os = "macos"))]
#[test]
fn test_monotonic_raw() {
let monotonic_raw = MonotonicRaw;
let now = monotonic_raw.get_time();
let later = monotonic_raw.get_time();
assert!(now < later);
}
#[rstest]
#[case::no_tsc_change(
TscCount::new(0),
Instant::from_secs(0),
Period::from_seconds(1e-9),
0,
Instant::from_secs(0)
)]
#[case::start_from_zero_time(
TscCount::new(0),
Instant::from_secs(0),
Period::from_seconds(1e-9),
1_000_000_000,
Instant::from_secs(1)
)]
#[case::start_from_nonzero_time(
TscCount::new(0),
Instant::from_secs(1),
Period::from_seconds(1e-9),
1_000_000_000,
Instant::from_secs(2)
)]
#[case::larger_period(
TscCount::new(0),
Instant::from_secs(0),
Period::from_seconds(1e-6),
1_000_000_000,
Instant::from_secs(1_000)
)]
#[case::start_from_nonzero_tsc(
TscCount::new(1_000_000_000),
Instant::from_secs(0),
Period::from_seconds(1e-9),
2_000_000_000,
Instant::from_secs(1)
)]
fn test_clockbound_clock(
#[case] initial_tsc: TscCount,
#[case] initial_time: Instant,
#[case] period: Period,
#[case] read_tsc_output: u64,
#[case] expected_time: Instant,
) {
let mut mock_read_tsc = MockReadTsc::new();
mock_read_tsc
.expect_read_tsc()
.returning(move || read_tsc_output);
let tsc_count = initial_tsc;
let time = initial_time;
let clock_error_bound = Duration::new(0);
let period_max_error = Period::from_seconds(0.0);
let as_of_monotonic = Instant::from_secs(2);
let clock_parameters = ClockParameters {
tsc_count,
time,
clock_error_bound,
period,
period_max_error,
as_of_monotonic,
};
let clockbound_clock = ClockBound::new(clock_parameters, mock_read_tsc);
assert_eq!(clockbound_clock.get_time(), expected_time);
}
}