Skip to main content

laser_sdk/agent/
clock.rs

1use std::sync::atomic::{AtomicU64, Ordering};
2
3/// A source of the current time in epoch microseconds. The seam the SLA timer
4/// and any deadline check read, so a test can drive time deterministically
5/// instead of sleeping. Mirrors the `Deduplicator` and `StateStore` seams: one
6/// trait, a real implementation, and a test double.
7pub trait Clock: Send + Sync {
8    /// The current time, epoch microseconds.
9    fn now_micros(&self) -> u64;
10}
11
12/// The real clock, reading the same epoch-microsecond time the substrate stamps.
13#[derive(Debug, Clone, Copy, Default)]
14pub struct SystemClock;
15
16impl Clock for SystemClock {
17    fn now_micros(&self) -> u64 {
18        iggy::prelude::IggyTimestamp::now().as_micros()
19    }
20}
21
22/// A test clock whose time is set and advanced explicitly, so a deadline or SLA
23/// test fires on demand without sleeping. Cheap to share (`&TestClock`).
24#[derive(Debug, Default)]
25pub struct TestClock {
26    now_micros: AtomicU64,
27}
28
29impl TestClock {
30    /// A test clock starting at `start_micros`.
31    pub fn new(start_micros: u64) -> Self {
32        Self {
33            now_micros: AtomicU64::new(start_micros),
34        }
35    }
36
37    /// Move time forward by `by_micros`.
38    pub fn advance(&self, by_micros: u64) {
39        self.now_micros.fetch_add(by_micros, Ordering::Relaxed);
40    }
41
42    /// Set the absolute time.
43    pub fn set(&self, now_micros: u64) {
44        self.now_micros.store(now_micros, Ordering::Relaxed);
45    }
46}
47
48impl Clock for TestClock {
49    fn now_micros(&self) -> u64 {
50        self.now_micros.load(Ordering::Relaxed)
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn given_a_test_clock_when_advanced_then_should_report_the_set_time() {
60        let clock = TestClock::new(1_000);
61        assert_eq!(clock.now_micros(), 1_000);
62        clock.advance(500);
63        assert_eq!(clock.now_micros(), 1_500);
64        clock.set(42);
65        assert_eq!(clock.now_micros(), 42);
66    }
67}