This repository has no description
2.3 kB
84 lines
1use std::sync::Arc;
2use std::sync::atomic::{AtomicU64, Ordering};
3use std::time::{Duration, SystemTime, UNIX_EPOCH};
4
5pub use knot_types::UnixMicros;
6
7pub trait Clock: Send + Sync + 'static {
8 fn now_unix_micros(&self) -> UnixMicros;
9}
10
11impl<T: Clock + ?Sized> Clock for Arc<T> {
12 fn now_unix_micros(&self) -> UnixMicros {
13 (**self).now_unix_micros()
14 }
15}
16
17pub struct SystemClock;
18
19impl Clock for SystemClock {
20 fn now_unix_micros(&self) -> UnixMicros {
21 let micros = SystemTime::now()
22 .duration_since(UNIX_EPOCH)
23 .map(|elapsed| elapsed.as_micros() as u64)
24 .unwrap_or(0);
25 UnixMicros::new(micros)
26 }
27}
28
29pub struct ManualClock {
30 micros: AtomicU64,
31}
32
33impl ManualClock {
34 pub fn new(start: UnixMicros) -> Self {
35 Self {
36 micros: AtomicU64::new(start.get()),
37 }
38 }
39
40 pub fn advance(&self, delta: Duration) {
41 self.micros
42 .fetch_add(delta.as_micros() as u64, Ordering::SeqCst);
43 }
44}
45
46impl Clock for ManualClock {
47 fn now_unix_micros(&self) -> UnixMicros {
48 UnixMicros::new(self.micros.load(Ordering::SeqCst))
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn manual_clock_advances() {
58 let clock = ManualClock::new(UnixMicros::new(1_000));
59 assert_eq!(clock.now_unix_micros().get(), 1_000);
60 clock.advance(Duration::from_micros(500));
61 assert_eq!(clock.now_unix_micros().get(), 1_500);
62 }
63
64 #[test]
65 fn manual_clock_same_start_same_sequence() {
66 let one = ManualClock::new(UnixMicros::new(1_000));
67 let two = ManualClock::new(UnixMicros::new(1_000));
68 let advances = [10, 250, 7, 1_000];
69 advances.iter().for_each(|&step| {
70 one.advance(Duration::from_micros(step));
71 two.advance(Duration::from_micros(step));
72 assert_eq!(one.now_unix_micros(), two.now_unix_micros());
73 });
74 }
75
76 #[test]
77 fn a_shared_clock_advances_through_the_trait_object() {
78 let shared = Arc::new(ManualClock::new(UnixMicros::new(1_000)));
79 let view: Arc<dyn Clock> = Arc::clone(&shared) as Arc<dyn Clock>;
80 assert_eq!(view.now_unix_micros().get(), 1_000);
81 shared.advance(Duration::from_micros(250));
82 assert_eq!(view.now_unix_micros().get(), 1_250);
83 }
84}