Skip to main content

moonpool_core/
time.rs

1//! Time provider abstraction for simulation and real time.
2//!
3//! This module provides a unified interface for time operations that works
4//! seamlessly with both simulation time and real wall-clock time.
5
6use async_trait::async_trait;
7use std::time::Duration;
8use thiserror::Error;
9
10/// Errors that can occur during time operations.
11#[derive(Debug, Clone, PartialEq, Eq, Error)]
12pub enum TimeError {
13    /// The operation timed out.
14    #[error("operation timed out")]
15    Elapsed,
16
17    /// The time provider has been shut down and is no longer accessible.
18    #[error("time provider shut down")]
19    Shutdown,
20}
21
22/// Provider trait for time operations.
23///
24/// This trait allows code to work with both simulation time and real wall-clock time
25/// in a unified way. Implementations handle sleeping and getting current time
26/// appropriate for their environment.
27///
28/// ## Time Semantics
29///
30/// - `now()`: Returns the exact, canonical time. Use this for scheduling and precise comparisons.
31/// - `timer()`: Returns a potentially drifted time that can be slightly ahead of `now()`.
32///   In simulation, this simulates real-world clock drift. In production, this equals `now()`.
33///
34/// FDB ref: sim2.actor.cpp:1056-1064
35#[async_trait(?Send)]
36pub trait TimeProvider: Clone {
37    /// Sleep for the specified duration.
38    ///
39    /// In simulation, this advances simulation time. In real time,
40    /// this uses actual wall-clock delays.
41    async fn sleep(&self, duration: Duration) -> Result<(), TimeError>;
42
43    /// Get exact current time.
44    ///
45    /// In simulation, this returns the canonical simulation time.
46    /// In real time, this returns wall-clock elapsed time since provider creation.
47    ///
48    /// Use this for precise time comparisons and event scheduling.
49    fn now(&self) -> Duration;
50
51    /// Get drifted timer time.
52    ///
53    /// In simulation, this can be up to `clock_drift_max` (default 100ms) ahead of `now()`.
54    /// This simulates real-world clock drift between processes.
55    /// In real time, this equals `now()`.
56    ///
57    /// Use this for time-sensitive application logic like timeouts, leases, and heartbeats
58    /// to test how code handles clock drift.
59    ///
60    /// FDB ref: sim2.actor.cpp:1058-1064
61    fn timer(&self) -> Duration;
62
63    /// Run a future with a timeout.
64    ///
65    /// Returns `Ok(result)` if the future completes within the timeout,
66    /// or `Err(TimeError::Elapsed)` if it times out.
67    async fn timeout<F, T>(&self, duration: Duration, future: F) -> Result<T, TimeError>
68    where
69        F: std::future::Future<Output = T>;
70}
71
72/// Real time provider using Tokio's time facilities.
73#[derive(Debug, Clone)]
74pub struct TokioTimeProvider {
75    /// Start time for calculating elapsed duration
76    start_time: std::time::Instant,
77}
78
79impl TokioTimeProvider {
80    /// Create a new Tokio time provider.
81    pub fn new() -> Self {
82        Self {
83            start_time: std::time::Instant::now(),
84        }
85    }
86}
87
88impl Default for TokioTimeProvider {
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94#[async_trait(?Send)]
95impl TimeProvider for TokioTimeProvider {
96    async fn sleep(&self, duration: Duration) -> Result<(), TimeError> {
97        tokio::time::sleep(duration).await;
98        Ok(())
99    }
100
101    fn now(&self) -> Duration {
102        // Return elapsed time since provider creation
103        self.start_time.elapsed()
104    }
105
106    fn timer(&self) -> Duration {
107        // In real time, there's no simulated drift - timer equals now
108        self.now()
109    }
110
111    async fn timeout<F, T>(&self, duration: Duration, future: F) -> Result<T, TimeError>
112    where
113        F: std::future::Future<Output = T>,
114    {
115        match tokio::time::timeout(duration, future).await {
116            Ok(result) => Ok(result),
117            Err(_) => Err(TimeError::Elapsed),
118        }
119    }
120}