nautilus_network/ratelimiter/
clock.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3//  https://poseitrader.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Time sources for rate limiters.
17//!
18//! The time sources contained in this module allow the rate limiter
19//! to be (optionally) independent of std, and additionally
20//! allow mocking the passage of time.
21//!
22//! You can supply a custom time source by implementing both [`Reference`]
23//! and [`Clock`] for your own types, and by implementing `Add<Nanos>` for
24//! your [`Reference`] type:
25use std::{
26    fmt::Debug,
27    ops::Add,
28    prelude::v1::*,
29    sync::{
30        Arc,
31        atomic::{AtomicU64, Ordering},
32    },
33    time::{Duration, Instant},
34};
35
36use super::nanos::Nanos;
37
38/// A measurement from a clock.
39pub trait Reference:
40    Sized + Add<Nanos, Output = Self> + PartialEq + Eq + Ord + Copy + Clone + Send + Sync + Debug
41{
42    /// Determines the time that separates two measurements of a
43    /// clock. Implementations of this must perform a saturating
44    /// subtraction - if the `earlier` timestamp should be later,
45    /// `duration_since` must return the zero duration.
46    fn duration_since(&self, earlier: Self) -> Nanos;
47
48    /// Returns a reference point that lies at most `duration` in the
49    /// past from the current reference. If an underflow should occur,
50    /// returns the current reference.
51    fn saturating_sub(&self, duration: Nanos) -> Self;
52}
53
54/// A time source used by rate limiters.
55pub trait Clock: Clone {
56    /// A measurement of a monotonically increasing clock.
57    type Instant: Reference;
58
59    /// Returns a measurement of the clock.
60    fn now(&self) -> Self::Instant;
61}
62
63impl Reference for Duration {
64    /// The internal duration between this point and another.
65    fn duration_since(&self, earlier: Self) -> Nanos {
66        self.checked_sub(earlier)
67            .unwrap_or_else(|| Self::new(0, 0))
68            .into()
69    }
70
71    /// The internal duration between this point and another.
72    fn saturating_sub(&self, duration: Nanos) -> Self {
73        self.checked_sub(duration.into()).unwrap_or(*self)
74    }
75}
76
77impl Add<Nanos> for Duration {
78    type Output = Self;
79
80    fn add(self, other: Nanos) -> Self {
81        let other: Self = other.into();
82        self + other
83    }
84}
85
86/// A mock implementation of a clock. All it does is keep track of
87/// what "now" is (relative to some point meaningful to the program),
88/// and returns that.
89///
90/// # Thread safety
91/// The mock time is represented as an atomic u64 count of nanoseconds, behind an [`Arc`].
92/// Clones of this clock will all show the same time, even if the original advances.
93#[derive(Debug, Clone, Default)]
94pub struct FakeRelativeClock {
95    now: Arc<AtomicU64>,
96}
97
98impl FakeRelativeClock {
99    /// Advances the fake clock by the given amount.
100    ///
101    /// # Panics
102    ///
103    /// Panics if `by` cannot be represented as a `u64` number of nanoseconds (i.e., exceeds 584 years).
104    pub fn advance(&self, by: Duration) {
105        let by: u64 = by
106            .as_nanos()
107            .try_into()
108            .expect("Cannot represent durations greater than 584 years");
109
110        let mut prev = self.now.load(Ordering::Acquire);
111        let mut next = prev + by;
112        while let Err(next_prev) =
113            self.now
114                .compare_exchange_weak(prev, next, Ordering::Release, Ordering::Relaxed)
115        {
116            prev = next_prev;
117            next = prev + by;
118        }
119    }
120}
121
122impl PartialEq for FakeRelativeClock {
123    fn eq(&self, other: &Self) -> bool {
124        self.now.load(Ordering::Relaxed) == other.now.load(Ordering::Relaxed)
125    }
126}
127
128impl Clock for FakeRelativeClock {
129    type Instant = Nanos;
130
131    fn now(&self) -> Self::Instant {
132        self.now.load(Ordering::Relaxed).into()
133    }
134}
135
136/// The monotonic clock implemented by [`Instant`].
137#[derive(Clone, Debug, Default)]
138pub struct MonotonicClock;
139
140impl Add<Nanos> for Instant {
141    type Output = Self;
142
143    fn add(self, other: Nanos) -> Self {
144        let other: Duration = other.into();
145        self + other
146    }
147}
148
149impl Reference for Instant {
150    fn duration_since(&self, earlier: Self) -> Nanos {
151        if earlier < *self {
152            (*self - earlier).into()
153        } else {
154            Nanos::from(Duration::new(0, 0))
155        }
156    }
157
158    fn saturating_sub(&self, duration: Nanos) -> Self {
159        self.checked_sub(duration.into()).unwrap_or(*self)
160    }
161}
162
163impl Clock for MonotonicClock {
164    type Instant = Instant;
165
166    fn now(&self) -> Self::Instant {
167        Instant::now()
168    }
169}
170
171////////////////////////////////////////////////////////////////////////////////
172// Tests
173////////////////////////////////////////////////////////////////////////////////
174#[cfg(test)]
175mod test {
176    use std::{sync::Arc, thread, time::Duration};
177
178    use rstest::rstest;
179
180    use super::*;
181
182    #[rstest]
183    fn fake_clock_parallel_advances() {
184        let clock = Arc::new(FakeRelativeClock::default());
185        let threads = std::iter::repeat_n((), 10)
186            .map(move |()| {
187                let clock = Arc::clone(&clock);
188                thread::spawn(move || {
189                    for _ in 0..1_000_000 {
190                        let now = clock.now();
191                        clock.advance(Duration::from_nanos(1));
192                        assert!(clock.now() > now);
193                    }
194                })
195            })
196            .collect::<Vec<_>>();
197        for t in threads {
198            t.join().unwrap();
199        }
200    }
201
202    #[rstest]
203    fn duration_addition_coverage() {
204        let d = Duration::from_secs(1);
205        let one_ns = Nanos::from(1);
206        assert!(d + one_ns > d);
207    }
208}