aeronet_transport/rtt.rs
1//! See [`RttEstimator`].
2
3use {core::time::Duration, typesize::derive::TypeSize};
4
5/// Computes an RTT estimation for a network path.
6///
7/// This is based on [`quinn-proto`'s `RttEstimator`](https://github.com/quinn-rs/quinn/blob/411abe9/quinn-proto/src/connection/paths.rs#L151).
8#[derive(Debug, Clone, TypeSize)]
9pub struct RttEstimator {
10 latest: Duration,
11 smoothed: Duration,
12 var: Duration,
13 min: Duration,
14}
15
16const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
17
18impl RttEstimator {
19 /// Creates a new estimator from a given initial RTT.
20 #[must_use]
21 pub fn new(initial_rtt: Duration) -> Self {
22 Self {
23 latest: initial_rtt,
24 smoothed: initial_rtt,
25 var: initial_rtt / 2,
26 min: initial_rtt,
27 }
28 }
29
30 /// Gets the current best RTT estimation.
31 #[must_use]
32 pub const fn get(&self) -> Duration {
33 self.smoothed
34 }
35
36 /// Gets a conservative estimate of RTT.
37 ///
38 /// Takes the maximum of smoothed and latest RTT, as recommended
39 /// in 6.1.2 of the recovery spec (draft 29).
40 #[must_use]
41 pub fn conservative(&self) -> Duration {
42 self.get().max(self.latest)
43 }
44
45 /// Gets the minimum RTT registered so far for this estimator.
46 ///
47 /// # Examples
48 ///
49 /// ```
50 /// # use {aeronet_transport::rtt::RttEstimator, core::time::Duration};
51 /// let mut rtt = RttEstimator::new(Duration::from_millis(500));
52 /// assert_eq!(Duration::from_millis(500), rtt.min());
53 ///
54 /// rtt.update(Duration::from_millis(750));
55 /// assert_eq!(Duration::from_millis(500), rtt.min());
56 ///
57 /// rtt.update(Duration::from_millis(250));
58 /// assert_eq!(Duration::from_millis(250), rtt.min());
59 /// ```
60 #[must_use]
61 pub const fn min(&self) -> Duration {
62 self.min
63 }
64
65 /// Computes the probe timeout duration (PTO) as described in
66 /// [RFC 9002 Section 6.2.1].
67 ///
68 /// [RFC 9002 Section 6.2.1]: https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1
69 #[must_use]
70 pub fn pto(&self) -> Duration {
71 self.get() + (self.var * 4).max(TIMER_GRANULARITY)
72 }
73
74 /// Adds an RTT sample to this estimation.
75 pub fn update(&mut self, rtt: Duration) {
76 self.latest = rtt;
77 self.min = self.min.min(rtt);
78
79 let var_sample = self.smoothed.abs_diff(rtt);
80 self.var = (3 * self.var + var_sample) / 4;
81 self.smoothed = (7 * self.smoothed + rtt) / 8;
82 }
83}
84
85/// Default initial RTT to use for [`RttEstimator`] before any RTT samples have
86/// been provided.
87///
88/// This value is based on [RFC 9002 Section 6.2.2].
89///
90/// [RFC 9002 Section 6.2.2]: https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.2-1
91pub const DEFAULT_INITIAL_RTT: Duration = Duration::from_millis(333);
92
93impl Default for RttEstimator {
94 fn default() -> Self {
95 Self::new(DEFAULT_INITIAL_RTT)
96 }
97}