1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! Round-trip time from ACK/ACKACK pairs (section 4.10).
/// Microseconds.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rtt {
rtt: i64,
var: i64,
sampled: bool,
}
impl Default for Rtt {
fn default() -> Self {
Self::new()
}
}
impl Rtt {
/// The specification's starting values: 100 ms, variance 50 ms.
pub const fn new() -> Self {
Self {
rtt: 100_000,
var: 50_000,
sampled: false,
}
}
/// The smoothed round-trip time, in microseconds.
pub fn rtt(&self) -> i64 {
self.rtt
}
/// The smoothed round-trip time variance (RTTVar), in microseconds.
pub fn var(&self) -> i64 {
self.var
}
/// Adds one measurement. The first replaces the starting values (as
/// libsrt's receiver does, observed: its ACK after the first ACKACK
/// reports that sample as the RTT and half of it as the variance);
/// later ones are smoothed, RTT by 1/8 and variance by 1/4.
pub fn sample(&mut self, rtt: i64) {
// Nothing real takes a minute; a stale ACKACK could.
let rtt = rtt.clamp(1, 60_000_000);
if !self.sampled {
self.sampled = true;
self.rtt = rtt;
self.var = rtt / 2;
return;
}
self.var = (3 * self.var + (self.rtt - rtt).abs()) / 4;
self.rtt = (7 * self.rtt + rtt) / 8;
}
/// How long to wait for a retransmission before asking again: the round
/// trip plus four times its variance, the specification's allowance for
/// a round trip that is late but normal. Asking again after exactly one
/// round trip raced retransmissions already on their way (measured in
/// `docs/interop.md`).
///
/// Just after the first sample the variance is half the RTT, so for the
/// first few ACKs (about 100 ms) this is up to three round trips; it
/// shrinks by a quarter with each sample after. A loss in that time is
/// asked for again later than it would be once the estimate settles.
pub fn retry(&self) -> i64 {
self.rtt + 4 * self.var
}
/// How often periodic NAK reports go out: (RTT + 4 * RTTVar) / 2, at
/// least 20 ms.
pub fn nak_interval(&self) -> i64 {
((self.rtt + 4 * self.var) / 2).max(20_000)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn first_sample_replaces_then_smooths() {
let mut r = Rtt::new();
assert_eq!((r.rtt(), r.var()), (100_000, 50_000));
r.sample(63_697);
assert_eq!((r.rtt(), r.var()), (63_697, 31_848));
r.sample(63_697);
assert_eq!(r.rtt(), 63_697);
assert_eq!(r.var(), 31_848 * 3 / 4);
r.sample(71_697);
assert_eq!(r.rtt(), 63_697 + 1_000);
}
#[test]
fn retry_allows_for_the_variance() {
let mut r = Rtt::new();
assert_eq!(r.retry(), 300_000);
r.sample(60_000);
assert_eq!(r.retry(), 60_000 + 4 * 30_000);
}
#[test]
fn nak_interval_has_a_floor() {
let mut r = Rtt::new();
r.sample(500);
assert_eq!(r.nak_interval(), 20_000);
let r = Rtt::new();
assert_eq!(r.nak_interval(), 150_000);
}
#[test]
fn absurd_samples_are_clamped() {
let mut r = Rtt::new();
r.sample(-5);
assert_eq!(r.rtt(), 1);
r.sample(i64::MAX);
assert!(r.rtt() < 10_000_000);
}
}