1use crate::config::ClockResolution;
8use std::sync::Arc;
9use std::time::Duration;
10use tokio::sync::broadcast;
11use tokio::time::Instant;
12use tracing::warn;
13
14#[derive(Debug, Clone)]
19pub struct TickEvent {
20 pub tick_count: u64,
22 pub timestamp: Instant,
24}
25
26#[doc(hidden)]
28pub(crate) struct SystemClock {
29 resolution: ClockResolution,
30 tick_sender: broadcast::Sender<Arc<TickEvent>>,
31}
32
33impl SystemClock {
34 pub(crate) fn new(
36 resolution: ClockResolution,
37 tick_sender: broadcast::Sender<Arc<TickEvent>>,
38 ) -> Self {
39 Self {
40 resolution,
41 tick_sender,
42 }
43 }
44
45 pub(crate) async fn run(&self, mut shutdown_rx: broadcast::Receiver<()>) {
47 let tick_duration = self.resolution.to_duration();
48 let mut interval = tokio::time::interval(tick_duration);
49 let mut tick_count = 0u64;
50
51 interval.tick().await; loop {
54 tokio::select! {
55 biased;
56 _ = shutdown_rx.recv() => break,
57 _ = interval.tick() => {
59 tick_count += 1;
60 let event = Arc::new(TickEvent {
61 tick_count,
62 timestamp: Instant::now(),
63 });
64
65 if self.tick_sender.send(event).is_err() {
66 warn!("No active subscribers for TickEvent. This may be normal during startup/shutdown.");
67 }
68 }
69 }
70 }
71 }
72}
73
74impl ClockResolution {
75 pub(crate) fn to_duration(&self) -> Duration {
77 let ticks_per_sec = match self {
78 ClockResolution::Ultra => 120,
79 ClockResolution::High => 60,
80 ClockResolution::Medium => 30,
81 ClockResolution::Low => 1,
82 ClockResolution::Custom { ticks_per_second } => *ticks_per_second,
83 };
84 if ticks_per_sec == 0 {
85 return Duration::from_secs(u64::MAX);
86 }
87 Duration::from_secs_f64(1.0 / ticks_per_sec as f64)
88 }
89}