quiche/recovery/
rtt.rs

1// Copyright (C) 2024, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use std::time::Duration;
28use std::time::Instant;
29
30use crate::minmax::Minmax;
31use crate::recovery::GRANULARITY;
32
33pub(crate) const RTT_WINDOW: Duration = Duration::from_secs(300);
34
35pub struct RttStats {
36    pub(super) latest_rtt: Duration,
37
38    max_rtt: Duration,
39
40    pub(super) smoothed_rtt: Duration,
41
42    pub(super) rttvar: Duration,
43
44    pub(super) min_rtt: Minmax<Duration>,
45
46    pub(super) max_ack_delay: Duration,
47
48    pub(super) has_first_rtt_sample: bool,
49}
50
51impl std::fmt::Debug for RttStats {
52    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
53        f.debug_struct("RttStats")
54            .field("lastest_rtt", &self.latest_rtt)
55            .field("srtt", &self.smoothed_rtt)
56            .field("minrtt", &*self.min_rtt)
57            .field("rttvar", &self.rttvar)
58            .finish()
59    }
60}
61
62impl RttStats {
63    pub(crate) fn new(initial_rtt: Duration, max_ack_delay: Duration) -> Self {
64        RttStats {
65            latest_rtt: Duration::ZERO,
66            min_rtt: Minmax::new(initial_rtt),
67            smoothed_rtt: initial_rtt,
68            max_rtt: initial_rtt,
69            rttvar: initial_rtt / 2,
70            has_first_rtt_sample: false,
71            max_ack_delay,
72        }
73    }
74
75    pub(crate) fn update_rtt(
76        &mut self, latest_rtt: Duration, mut ack_delay: Duration, now: Instant,
77        handshake_confirmed: bool,
78    ) {
79        self.latest_rtt = latest_rtt;
80
81        if !self.has_first_rtt_sample {
82            self.min_rtt.reset(now, latest_rtt);
83            self.smoothed_rtt = latest_rtt;
84            self.max_rtt = latest_rtt;
85            self.rttvar = latest_rtt / 2;
86            self.has_first_rtt_sample = true;
87            return;
88        }
89
90        // min_rtt ignores acknowledgment delay.
91        self.min_rtt.running_min(RTT_WINDOW, now, latest_rtt);
92
93        self.max_rtt = self.max_rtt.max(latest_rtt);
94
95        // Limit ack_delay by max_ack_delay after handshake confirmation.
96        if handshake_confirmed {
97            ack_delay = ack_delay.min(self.max_ack_delay);
98        }
99
100        // Adjust for acknowledgment delay if plausible.
101        let mut adjusted_rtt = latest_rtt;
102        if latest_rtt >= *self.min_rtt + ack_delay {
103            adjusted_rtt = latest_rtt - ack_delay;
104        }
105
106        self.rttvar = self.rttvar * 3 / 4 +
107            Duration::from_nanos(
108                self.smoothed_rtt
109                    .as_nanos()
110                    .abs_diff(adjusted_rtt.as_nanos()) as u64 /
111                    4,
112            );
113
114        self.smoothed_rtt = self.smoothed_rtt * 7 / 8 + adjusted_rtt / 8;
115    }
116
117    pub(crate) fn rtt(&self) -> Duration {
118        self.smoothed_rtt
119    }
120
121    #[allow(dead_code)]
122    pub(crate) fn latest_rtt(&self) -> Duration {
123        self.latest_rtt
124    }
125
126    pub(crate) fn rttvar(&self) -> Duration {
127        self.rttvar
128    }
129
130    pub(crate) fn min_rtt(&self) -> Option<Duration> {
131        if self.has_first_rtt_sample {
132            Some(*self.min_rtt)
133        } else {
134            None
135        }
136    }
137
138    pub(crate) fn max_rtt(&self) -> Option<Duration> {
139        if self.has_first_rtt_sample {
140            Some(self.max_rtt)
141        } else {
142            None
143        }
144    }
145
146    pub(crate) fn loss_delay(&self, time_thresh: f64) -> Duration {
147        self.latest_rtt
148            .max(self.smoothed_rtt)
149            .mul_f64(time_thresh)
150            .max(GRANULARITY)
151    }
152}