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