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 crate::dst::time::sleep(duration).await;
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use std::{sync::Arc, thread, time::Duration};
188
189 use rstest::rstest;
190
191 use super::*;
192
193 #[rstest]
194 fn fake_clock_parallel_advances() {
195 let clock = Arc::new(FakeRelativeClock::default());
196 let threads = std::iter::repeat_n((), 10)
197 .map(|()| {
198 let clock = Arc::clone(&clock);
199
200 thread::spawn(move || {
201 for _ in 0..1_000_000 {
202 let now = clock.now();
203 clock.advance(Duration::from_nanos(1));
204 assert!(clock.now() > now);
205 }
206 })
207 })
208 .collect::<Vec<_>>();
209
210 for t in threads {
211 t.join().unwrap();
212 }
213
214 assert_eq!(clock.now(), Nanos::new(10_000_000));
215 }
216
217 #[rstest]
218 fn duration_addition_coverage() {
219 let d = Duration::from_secs(1);
220 let one_ns = Nanos::from(1);
221 assert_eq!(d + one_ns, Duration::new(1, 1));
222 }
223
224 #[rstest]
225 #[case(12, 5, 7)]
226 #[case(12, 12, 0)]
227 #[case(5, 12, 0)]
228 fn duration_since_saturates(#[case] now: u64, #[case] earlier: u64, #[case] expected: u64) {
229 assert_eq!(
230 Reference::duration_since(&Duration::from_nanos(now), Duration::from_nanos(earlier)),
231 Nanos::new(expected)
232 );
233 }
234
235 #[rstest]
236 #[case(12, 5, 7)]
237 #[case(12, 12, 0)]
238 #[case(5, 12, 5)]
239 fn duration_subtraction_preserves_reference_on_underflow(
240 #[case] now: u64,
241 #[case] subtract: u64,
242 #[case] expected: u64,
243 ) {
244 assert_eq!(
245 Reference::saturating_sub(&Duration::from_nanos(now), Nanos::new(subtract)),
246 Duration::from_nanos(expected)
247 );
248 }
249
250 #[rstest]
251 #[tokio::test]
252 async fn fake_sleep_advances_shared_clock() {
253 let clock = FakeRelativeClock::default();
254 let clone = clock.clone();
255 clock.advance(Duration::from_nanos(13));
256
257 clone.sleep(Duration::from_nanos(29)).await;
258
259 assert_eq!(clock.now(), Nanos::new(42));
260 assert_eq!(clone.now(), Nanos::new(42));
261 assert_eq!(clock, clone);
262 assert_ne!(clock, FakeRelativeClock::default());
263 }
264
265 #[rstest]
266 #[should_panic(expected = "Cannot represent durations greater than 584 years")]
267 fn fake_clock_rejects_unrepresentable_duration() {
268 FakeRelativeClock::default().advance(Duration::MAX);
269 }
270
271 #[rstest]
272 #[cfg_attr(not(all(feature = "simulation", madsim)), tokio::test)]
273 #[cfg_attr(all(feature = "simulation", madsim), madsim::test)]
274 async fn instant_reference_arithmetic_preserves_exact_offsets() {
275 let start = Instant::now();
276 let later = start + Nanos::new(37);
277
278 assert_eq!(later, start + Duration::from_nanos(37));
279 assert_eq!(Reference::duration_since(&later, start), Nanos::new(37));
280 assert_eq!(Reference::duration_since(&start, later), Nanos::new(0));
281 assert_eq!(Reference::duration_since(&start, start), Nanos::new(0));
282 assert_eq!(Reference::saturating_sub(&later, Nanos::new(37)), start);
283 }
284
285 #[rstest]
286 #[cfg(not(all(feature = "simulation", madsim)))]
287 #[tokio::test(start_paused = true)]
288 async fn monotonic_sleep_advances_clock_by_requested_duration() {
289 let clock = MonotonicClock;
290 let start = clock.now();
291
292 clock.sleep(Duration::from_millis(37)).await;
293
294 assert_eq!(clock.now() - start, Duration::from_millis(37));
295 }
296
297 #[cfg(all(feature = "simulation", madsim))]
302 #[madsim::test]
303 async fn test_monotonic_clock_sleep_uses_virtual_time() {
304 let clock = MonotonicClock;
305 let start = Instant::now();
306 clock.sleep(Duration::from_millis(100)).await;
307 let elapsed = start.elapsed();
308 assert!(elapsed >= Duration::from_millis(100));
309 assert!(
310 elapsed < Duration::from_millis(101),
311 "virtual sleep showed real-tokio jitter: {elapsed:?}"
312 );
313 }
314}