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