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}