nautilus_network/ratelimiter/
clock.rs1use std::{
22 fmt::Debug,
23 future::Future,
24 ops::Add,
25 sync::{
26 Arc,
27 atomic::{AtomicU64, Ordering},
28 },
29 time::Duration,
30};
31
32use super::nanos::Nanos;
33use crate::dst::time::Instant;
34
35pub trait Reference:
37 Sized + Add<Nanos, Output = Self> + PartialEq + Eq + Ord + Copy + Clone + Send + Sync + Debug
38{
39 fn duration_since(&self, earlier: Self) -> Nanos;
44
45 #[must_use]
49 fn saturating_sub(&self, duration: Nanos) -> Self;
50}
51
52pub trait Clock: Clone {
54 type Instant: Reference;
56
57 fn now(&self) -> Self::Instant;
59
60 fn sleep(&self, duration: Duration) -> impl Future<Output = ()> + Send + '_;
66}
67
68impl Reference for Duration {
69 fn duration_since(&self, earlier: Self) -> Nanos {
71 (*self).saturating_sub(earlier).into()
72 }
73
74 fn saturating_sub(&self, duration: Nanos) -> Self {
76 self.checked_sub(duration.into()).unwrap_or(*self)
77 }
78}
79
80impl Add<Nanos> for Duration {
81 type Output = Self;
82
83 fn add(self, other: Nanos) -> Self {
84 let other: Self = other.into();
85 self + other
86 }
87}
88
89#[derive(Debug, Clone, Default)]
98pub struct FakeRelativeClock {
99 now: Arc<AtomicU64>,
100}
101
102impl FakeRelativeClock {
103 pub fn advance(&self, by: Duration) {
109 let by: u64 = by
110 .as_nanos()
111 .try_into()
112 .expect("Cannot represent durations greater than 584 years");
113
114 let mut prev = self.now.load(Ordering::Acquire);
115 let mut next = prev + by;
116
117 while let Err(e) =
118 self.now
119 .compare_exchange_weak(prev, next, Ordering::Release, Ordering::Relaxed)
120 {
121 prev = e;
122 next = prev + by;
123 }
124 }
125}
126
127impl PartialEq for FakeRelativeClock {
128 fn eq(&self, other: &Self) -> bool {
129 self.now.load(Ordering::Relaxed) == other.now.load(Ordering::Relaxed)
130 }
131}
132
133impl Clock for FakeRelativeClock {
134 type Instant = Nanos;
135
136 fn now(&self) -> Self::Instant {
137 self.now.load(Ordering::Relaxed).into()
138 }
139
140 fn sleep(&self, duration: Duration) -> impl Future<Output = ()> + Send + '_ {
141 self.advance(duration);
142 std::future::ready(())
143 }
144}
145
146#[derive(Clone, Debug, Default)]
148pub struct MonotonicClock;
149
150impl Add<Nanos> for Instant {
151 type Output = Self;
152
153 fn add(self, other: Nanos) -> Self {
154 let other: Duration = other.into();
155 self + other
156 }
157}
158
159impl Reference for Instant {
160 fn duration_since(&self, earlier: Self) -> Nanos {
161 if earlier < *self {
162 (*self - earlier).into()
163 } else {
164 Nanos::from(Duration::new(0, 0))
165 }
166 }
167
168 fn saturating_sub(&self, duration: Nanos) -> Self {
169 self.checked_sub(duration.into()).unwrap_or(*self)
170 }
171}
172
173impl Clock for MonotonicClock {
174 type Instant = Instant;
175
176 fn now(&self) -> Self::Instant {
177 Instant::now()
178 }
179
180 async fn sleep(&self, duration: Duration) {
181 #[cfg(not(all(feature = "simulation", madsim)))]
182 tokio::time::sleep(duration).await;
183 #[cfg(all(feature = "simulation", madsim))]
184 madsim::time::sleep(duration).await;
185 }
186}
187
188#[cfg(test)]
189mod test {
190 use std::{sync::Arc, thread, time::Duration};
191
192 use rstest::rstest;
193
194 use super::*;
195
196 #[rstest]
197 fn fake_clock_parallel_advances() {
198 let clock = Arc::new(FakeRelativeClock::default());
199 let threads = std::iter::repeat_n((), 10)
200 .map(move |()| {
201 let clock = Arc::clone(&clock);
202
203 thread::spawn(move || {
204 for _ in 0..1_000_000 {
205 let now = clock.now();
206 clock.advance(Duration::from_nanos(1));
207 assert!(clock.now() > now);
208 }
209 })
210 })
211 .collect::<Vec<_>>();
212
213 for t in threads {
214 t.join().unwrap();
215 }
216 }
217
218 #[rstest]
219 fn duration_addition_coverage() {
220 let d = Duration::from_secs(1);
221 let one_ns = Nanos::from(1);
222 assert!(d + one_ns > d);
223 }
224
225 #[cfg(all(feature = "simulation", madsim))]
230 #[madsim::test]
231 async fn test_monotonic_clock_sleep_uses_virtual_time() {
232 let clock = MonotonicClock;
233 let start = Instant::now();
234 clock.sleep(Duration::from_millis(100)).await;
235 let elapsed = start.elapsed();
236 assert!(elapsed >= Duration::from_millis(100));
237 assert!(
238 elapsed < Duration::from_millis(101),
239 "virtual sleep showed real-tokio jitter: {elapsed:?}"
240 );
241 }
242}