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 while let Err(e) =
117 self.now
118 .compare_exchange_weak(prev, next, Ordering::Release, Ordering::Relaxed)
119 {
120 prev = e;
121 next = prev + by;
122 }
123 }
124}
125
126impl PartialEq for FakeRelativeClock {
127 fn eq(&self, other: &Self) -> bool {
128 self.now.load(Ordering::Relaxed) == other.now.load(Ordering::Relaxed)
129 }
130}
131
132impl Clock for FakeRelativeClock {
133 type Instant = Nanos;
134
135 fn now(&self) -> Self::Instant {
136 self.now.load(Ordering::Relaxed).into()
137 }
138
139 fn sleep(&self, duration: Duration) -> impl Future<Output = ()> + Send + '_ {
140 self.advance(duration);
141 std::future::ready(())
142 }
143}
144
145#[derive(Clone, Debug, Default)]
147pub struct MonotonicClock;
148
149impl Add<Nanos> for Instant {
150 type Output = Self;
151
152 fn add(self, other: Nanos) -> Self {
153 let other: Duration = other.into();
154 self + other
155 }
156}
157
158impl Reference for Instant {
159 fn duration_since(&self, earlier: Self) -> Nanos {
160 if earlier < *self {
161 (*self - earlier).into()
162 } else {
163 Nanos::from(Duration::new(0, 0))
164 }
165 }
166
167 fn saturating_sub(&self, duration: Nanos) -> Self {
168 self.checked_sub(duration.into()).unwrap_or(*self)
169 }
170}
171
172impl Clock for MonotonicClock {
173 type Instant = Instant;
174
175 fn now(&self) -> Self::Instant {
176 Instant::now()
177 }
178
179 async fn sleep(&self, duration: Duration) {
180 crate::dst::time::sleep(duration).await;
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use std::{sync::Arc, thread, time::Duration};
187
188 use rstest::rstest;
189
190 use super::*;
191
192 #[rstest]
193 fn fake_clock_parallel_advances() {
194 let clock = Arc::new(FakeRelativeClock::default());
195 let threads = std::iter::repeat_n((), 10)
196 .map(|()| {
197 let clock = Arc::clone(&clock);
198
199 thread::spawn(move || {
200 for _ in 0..1_000_000 {
201 let now = clock.now();
202 clock.advance(Duration::from_nanos(1));
203 assert!(clock.now() > now);
204 }
205 })
206 })
207 .collect::<Vec<_>>();
208
209 for t in threads {
210 t.join().unwrap();
211 }
212
213 assert_eq!(clock.now(), Nanos::new(10_000_000));
214 }
215
216 #[rstest]
217 fn duration_addition_coverage() {
218 let d = Duration::from_secs(1);
219 let one_ns = Nanos::from(1);
220 assert_eq!(d + one_ns, Duration::new(1, 1));
221 }
222
223 #[rstest]
224 #[case(12, 5, 7)]
225 #[case(12, 12, 0)]
226 #[case(5, 12, 0)]
227 fn duration_since_saturates(#[case] now: u64, #[case] earlier: u64, #[case] expected: u64) {
228 assert_eq!(
229 Reference::duration_since(&Duration::from_nanos(now), Duration::from_nanos(earlier)),
230 Nanos::new(expected)
231 );
232 }
233
234 #[rstest]
235 #[case(12, 5, 7)]
236 #[case(12, 12, 0)]
237 #[case(5, 12, 5)]
238 fn duration_subtraction_preserves_reference_on_underflow(
239 #[case] now: u64,
240 #[case] subtract: u64,
241 #[case] expected: u64,
242 ) {
243 assert_eq!(
244 Reference::saturating_sub(&Duration::from_nanos(now), Nanos::new(subtract)),
245 Duration::from_nanos(expected)
246 );
247 }
248
249 #[rstest]
250 #[tokio::test]
251 async fn fake_sleep_advances_shared_clock() {
252 let clock = FakeRelativeClock::default();
253 let clone = clock.clone();
254 clock.advance(Duration::from_nanos(13));
255
256 clone.sleep(Duration::from_nanos(29)).await;
257
258 assert_eq!(clock.now(), Nanos::new(42));
259 assert_eq!(clone.now(), Nanos::new(42));
260 assert_eq!(clock, clone);
261 assert_ne!(clock, FakeRelativeClock::default());
262 }
263
264 #[rstest]
265 #[should_panic(expected = "Cannot represent durations greater than 584 years")]
266 fn fake_clock_rejects_unrepresentable_duration() {
267 FakeRelativeClock::default().advance(Duration::MAX);
268 }
269
270 #[rstest]
271 #[cfg_attr(not(all(feature = "simulation", madsim)), tokio::test)]
272 #[cfg_attr(all(feature = "simulation", madsim), madsim::test)]
273 async fn instant_reference_arithmetic_preserves_exact_offsets() {
274 let start = Instant::now();
275 let later = start + Nanos::new(37);
276
277 assert_eq!(later, start + Duration::from_nanos(37));
278 assert_eq!(Reference::duration_since(&later, start), Nanos::new(37));
279 assert_eq!(Reference::duration_since(&start, later), Nanos::new(0));
280 assert_eq!(Reference::duration_since(&start, start), Nanos::new(0));
281 assert_eq!(Reference::saturating_sub(&later, Nanos::new(37)), start);
282 }
283
284 #[rstest]
285 #[cfg(not(all(feature = "simulation", madsim)))]
286 #[tokio::test(start_paused = true)]
287 async fn monotonic_sleep_advances_clock_by_requested_duration() {
288 let clock = MonotonicClock;
289 let start = clock.now();
290
291 clock.sleep(Duration::from_millis(37)).await;
292
293 assert_eq!(clock.now() - start, Duration::from_millis(37));
294 }
295
296 #[cfg(all(feature = "simulation", madsim))]
301 #[madsim::test]
302 async fn test_monotonic_clock_sleep_uses_virtual_time() {
303 let clock = MonotonicClock;
304 let start = Instant::now();
305 clock.sleep(Duration::from_millis(100)).await;
306 let elapsed = start.elapsed();
307 assert!(elapsed >= Duration::from_millis(100));
308 assert!(
309 elapsed < Duration::from_millis(101),
310 "virtual sleep showed real-tokio jitter: {elapsed:?}"
311 );
312 }
313}