use crate::runtime::clock::{Clock, CycleTime};
use std::time::{Duration, Instant};
use time::OffsetDateTime;
#[cfg(target_os = "linux")]
const DEFAULT_MEASUREMENT_THRESHOLD_NS: u64 = 500;
#[cfg(target_os = "windows")]
const DEFAULT_MEASUREMENT_THRESHOLD_NS: u64 = 2000;
#[cfg(target_os = "macos")]
const DEFAULT_MEASUREMENT_THRESHOLD_NS: u64 = 1000;
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
const DEFAULT_MEASUREMENT_THRESHOLD_NS: u64 = 1500;
#[cfg(target_os = "linux")]
const DEFAULT_MAX_RETRIES: u32 = 5;
#[cfg(target_os = "windows")]
const DEFAULT_MAX_RETRIES: u32 = 20;
#[cfg(target_os = "macos")]
const DEFAULT_MAX_RETRIES: u32 = 10;
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
const DEFAULT_MAX_RETRIES: u32 = 15;
const DEFAULT_RESYNC_INTERVAL_SECS: u64 = 3600;
pub struct PrecisionClock {
base_wall_time: OffsetDateTime,
base_instant: Instant,
last_resync: Instant,
resync_interval: Duration,
measurement_threshold: Duration,
max_measurement_retries: u32,
is_accurate: bool,
}
impl PrecisionClock {
pub fn new() -> Self {
let mut clock = Self {
base_wall_time: OffsetDateTime::now_utc(),
base_instant: Instant::now(),
last_resync: Instant::now(),
resync_interval: Duration::from_secs(DEFAULT_RESYNC_INTERVAL_SECS),
measurement_threshold: Duration::from_nanos(DEFAULT_MEASUREMENT_THRESHOLD_NS),
max_measurement_retries: DEFAULT_MAX_RETRIES,
is_accurate: false,
};
clock.resync_with_quality_control();
clock
}
pub fn with_config(
resync_interval: Duration,
measurement_threshold: Duration,
max_retries: u32,
) -> Self {
let mut clock = Self {
base_wall_time: OffsetDateTime::now_utc(),
base_instant: Instant::now(),
last_resync: Instant::now(),
resync_interval,
measurement_threshold,
max_measurement_retries: max_retries,
is_accurate: false,
};
clock.resync_with_quality_control();
clock
}
#[inline]
pub fn resync(&mut self) {
self.resync_with_quality_control();
}
pub const fn is_accurate(&self) -> bool {
self.is_accurate
}
pub const fn measurement_threshold(&self) -> Duration {
self.measurement_threshold
}
fn resync_with_quality_control(&mut self) {
let mut best_measurement: Option<(Instant, OffsetDateTime, Duration)> = None;
let mut best_variance = Duration::from_secs(1);
for attempt in 0..self.max_measurement_retries {
let instant_before = Instant::now();
let wall_time = OffsetDateTime::now_utc();
let instant_after = Instant::now();
let measurement_variance = instant_after - instant_before;
if measurement_variance < best_variance {
best_variance = measurement_variance;
let estimated_instant = instant_before + measurement_variance / 2;
best_measurement = Some((estimated_instant, wall_time, measurement_variance));
if measurement_variance <= self.measurement_threshold {
break;
}
}
if attempt < self.max_measurement_retries - 1 {
std::hint::spin_loop();
}
}
if let Some((estimated_instant, wall_time, variance)) = best_measurement {
self.base_wall_time = wall_time;
self.base_instant = estimated_instant;
self.last_resync = estimated_instant;
self.is_accurate = variance <= self.measurement_threshold;
#[cfg(debug_assertions)]
if !self.is_accurate {
eprintln!(
"PrecisionClock: Warning - could not achieve desired accuracy. \
Best measurement: {:?}, threshold: {:?}",
variance, self.measurement_threshold
);
}
} else {
let now_instant = Instant::now();
self.base_wall_time = OffsetDateTime::now_utc();
self.base_instant = now_instant;
self.last_resync = now_instant;
self.is_accurate = false;
}
}
#[inline]
fn check_and_resync(&mut self, now: Instant) {
if now.duration_since(self.last_resync) >= self.resync_interval {
self.resync_with_quality_control();
}
}
}
impl Clock for PrecisionClock {
fn cycle_time(&mut self) -> CycleTime {
let now_instant = Instant::now();
self.check_and_resync(now_instant);
let elapsed_since_baseline = now_instant.duration_since(self.base_instant);
let estimated_cycle_time = self.base_wall_time + elapsed_since_baseline;
CycleTime {
instant: now_instant,
unix_time: estimated_cycle_time,
}
}
}
impl Default for PrecisionClock {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn test_precision_clock_creation() {
let clock = PrecisionClock::new();
assert_eq!(
clock.resync_interval,
Duration::from_secs(DEFAULT_RESYNC_INTERVAL_SECS)
);
assert_eq!(
clock.measurement_threshold,
Duration::from_nanos(DEFAULT_MEASUREMENT_THRESHOLD_NS)
);
assert_eq!(clock.max_measurement_retries, DEFAULT_MAX_RETRIES);
}
#[test]
fn test_custom_configuration() {
let clock = PrecisionClock::with_config(
Duration::from_secs(1800), Duration::from_nanos(500), 50, );
assert_eq!(clock.resync_interval, Duration::from_secs(1800));
assert_eq!(clock.measurement_threshold, Duration::from_nanos(500));
assert_eq!(clock.max_measurement_retries, 50);
}
#[test]
fn test_time_advances() {
let mut clock = PrecisionClock::new();
let time1 = clock.cycle_time();
thread::sleep(Duration::from_millis(1));
let time2 = clock.cycle_time();
assert!(time2.instant > time1.instant);
assert!(time2.unix_time > time1.unix_time);
}
#[test]
fn test_manual_resync() {
let mut clock = PrecisionClock::new();
let initial_accuracy = clock.is_accurate();
clock.resync();
let time = clock.cycle_time();
assert!(time.unix_time.unix_timestamp() > 0);
assert!(clock.is_accurate() == initial_accuracy || !clock.is_accurate());
}
#[test]
fn test_time_consistency() {
let mut clock = PrecisionClock::new();
let times: Vec<_> = (0..10).map(|_| clock.cycle_time()).collect();
for window in times.windows(2) {
assert!(window[1].instant >= window[0].instant);
assert!(window[1].unix_time >= window[0].unix_time);
}
}
#[test]
fn test_measurement_quality_reporting() {
let clock = PrecisionClock::new();
let _is_accurate = clock.is_accurate();
let _threshold = clock.measurement_threshold();
}
#[test]
fn test_os_specific_defaults() {
let clock = PrecisionClock::new();
#[cfg(target_os = "linux")]
{
assert_eq!(clock.measurement_threshold, Duration::from_nanos(500));
assert_eq!(clock.max_measurement_retries, 5);
}
#[cfg(target_os = "windows")]
{
assert_eq!(clock.measurement_threshold, Duration::from_nanos(2000));
assert_eq!(clock.max_measurement_retries, 20);
}
#[cfg(target_os = "macos")]
{
assert_eq!(clock.measurement_threshold, Duration::from_nanos(1000));
assert_eq!(clock.max_measurement_retries, 10);
}
}
}