use std::time::Duration;
#[derive(Debug)]
pub(crate) struct MonotonicClock {
clock: quanta::Clock,
epoch: u64,
}
impl MonotonicClock {
pub(crate) fn new() -> Self {
let clock = quanta::Clock::new();
let epoch = clock.raw();
Self { clock, epoch }
}
pub(crate) fn now(&self) -> u64 {
self.clock.delta_as_nanos(self.epoch, self.clock.raw())
}
#[cfg(test)]
pub(crate) fn elapsed_since(&self, start: u64) -> u64 {
self.now().saturating_sub(start)
}
}
pub(crate) fn precision_sleep(target_ns: u64, clock: &MonotonicClock) {
let now = clock.now();
if target_ns <= now {
return;
}
let remaining_ns = target_ns - now;
spin_sleep::sleep(Duration::from_nanos(remaining_ns));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_monotonic_clock_advances() {
let clock = MonotonicClock::new();
let t1 = clock.now();
std::hint::spin_loop();
let t2 = clock.now();
assert!(t2 > t1, "expected clock to advance: t1={t1}, t2={t2}");
}
#[test]
fn test_elapsed_since() {
let clock = MonotonicClock::new();
let start = clock.now();
std::thread::sleep(Duration::from_millis(1));
let elapsed = clock.elapsed_since(start);
assert!(elapsed > 0, "expected positive elapsed time, got {elapsed}");
}
#[test]
fn test_precision_sleep_accuracy() {
let clock = MonotonicClock::new();
let before = clock.now();
let target = before + 10_000_000; precision_sleep(target, &clock);
let after = clock.now();
let elapsed_ms = (after - before) / 1_000_000;
assert!(
(8..=30).contains(&elapsed_ms),
"expected ~10 ms sleep, got {elapsed_ms} ms"
);
}
#[test]
fn test_precision_sleep_no_undershoot() {
let clock = MonotonicClock::new();
let before = clock.now();
let target = before + 5_000_000; precision_sleep(target, &clock);
let after = clock.now();
let elapsed_ns = after - before;
assert!(
elapsed_ns >= 5_000_000,
"expected >= 5 ms, got {elapsed_ns} ns ({} ms)",
elapsed_ns / 1_000_000
);
}
#[test]
fn test_precision_sleep_past_target_returns_immediately() {
let clock = MonotonicClock::new();
std::thread::sleep(Duration::from_millis(1));
let past_target = 0; let before = clock.now();
precision_sleep(past_target, &clock);
let after = clock.now();
let elapsed_ns = after - before;
assert!(
elapsed_ns < 1_000_000,
"expected < 1 ms for past target, got {elapsed_ns} ns"
);
}
}