use crate::config::ClockResolution;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::broadcast;
use tokio::time::Instant;
use tracing::warn;
#[derive(Debug, Clone)]
pub struct TickEvent {
pub tick_count: u64,
pub timestamp: Instant,
}
#[doc(hidden)]
pub(crate) struct SystemClock {
resolution: ClockResolution,
tick_sender: broadcast::Sender<Arc<TickEvent>>,
}
impl SystemClock {
pub(crate) fn new(
resolution: ClockResolution,
tick_sender: broadcast::Sender<Arc<TickEvent>>,
) -> Self {
Self {
resolution,
tick_sender,
}
}
pub(crate) async fn run(&self, mut shutdown_rx: broadcast::Receiver<()>) {
let tick_duration = self.resolution.to_duration();
let mut interval = tokio::time::interval(tick_duration);
let mut tick_count = 0u64;
interval.tick().await;
loop {
tokio::select! {
biased;
_ = shutdown_rx.recv() => break,
_ = interval.tick() => {
tick_count += 1;
let event = Arc::new(TickEvent {
tick_count,
timestamp: Instant::now(),
});
if self.tick_sender.send(event).is_err() {
warn!("No active subscribers for TickEvent. This may be normal during startup/shutdown.");
}
}
}
}
}
}
impl ClockResolution {
pub(crate) fn to_duration(&self) -> Duration {
let ticks_per_sec = match self {
ClockResolution::Ultra => 120,
ClockResolution::High => 60,
ClockResolution::Medium => 30,
ClockResolution::Low => 1,
ClockResolution::Custom { ticks_per_second } => *ticks_per_second,
};
if ticks_per_sec == 0 {
return Duration::from_secs(u64::MAX);
}
Duration::from_secs_f64(1.0 / ticks_per_sec as f64)
}
}