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}