quiche/recovery/gcongestion/bbr2/rtt_jump_detector/
mod.rs1use std::time::Duration;
28use std::time::Instant;
29
30use crate::recovery::gcongestion::BbrRttJumpDetector;
31
32mod global_min;
33mod hmm;
34
35use self::global_min::GlobalMinDetector;
36use self::hmm::HmmDetector;
37
38#[derive(Debug)]
39pub(super) struct RttJumpDetector {
40 persistent_jump_count: u64,
42 last_persistent_jump_time: Option<Instant>,
45 detector: ActiveRttJumpDetector,
47}
48
49#[derive(Debug)]
50enum ActiveRttJumpDetector {
51 Disabled,
52 GlobalMin(GlobalMinDetector),
53 Hmm(HmmDetector),
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57enum RttJumpUpdate {
58 None,
59 PersistentConfirmed { episode_start_time: Instant },
60}
61
62impl RttJumpDetector {
63 pub(super) fn new(mode: BbrRttJumpDetector) -> Self {
64 let detector = match mode {
65 BbrRttJumpDetector::Disabled => ActiveRttJumpDetector::Disabled,
66 BbrRttJumpDetector::GlobalMin =>
67 ActiveRttJumpDetector::GlobalMin(GlobalMinDetector::default()),
68 BbrRttJumpDetector::Hmm =>
69 ActiveRttJumpDetector::Hmm(HmmDetector::default()),
70 };
71
72 Self {
73 persistent_jump_count: 0,
74 last_persistent_jump_time: None,
75 detector,
76 }
77 }
78
79 pub(super) fn on_rtt_sample(
81 &mut self, rtt_sample: Duration, event_time: Instant,
82 full_bandwidth_reached: bool,
83 ) {
84 let update = match &mut self.detector {
85 ActiveRttJumpDetector::Disabled => RttJumpUpdate::None,
86 ActiveRttJumpDetector::GlobalMin(detector) => detector.on_rtt_sample(
87 rtt_sample,
88 event_time,
89 full_bandwidth_reached,
90 ),
91 ActiveRttJumpDetector::Hmm(detector) => detector.on_rtt_sample(
92 rtt_sample,
93 event_time,
94 full_bandwidth_reached,
95 ),
96 };
97
98 self.apply_update(update);
99 }
100
101 fn apply_update(&mut self, update: RttJumpUpdate) {
102 match update {
103 RttJumpUpdate::None => {},
104 RttJumpUpdate::PersistentConfirmed { episode_start_time } => {
105 self.persistent_jump_count += 1;
106 self.last_persistent_jump_time = Some(episode_start_time);
107 },
108 }
109 }
110
111 pub(super) fn rtt_persistent_jump_count(&self) -> u64 {
113 self.persistent_jump_count
114 }
115
116 #[cfg(test)]
119 pub(super) fn last_persistent_jump_time(&self) -> Option<Instant> {
120 self.last_persistent_jump_time
121 }
122
123 #[cfg(test)]
125 pub(super) fn is_rtt_jump_active(&self) -> bool {
126 match &self.detector {
127 ActiveRttJumpDetector::Disabled => false,
128 ActiveRttJumpDetector::GlobalMin(detector) =>
129 detector.is_rtt_jump_active(),
130 ActiveRttJumpDetector::Hmm(detector) => detector.is_rtt_jump_active(),
131 }
132 }
133
134 #[cfg(test)]
136 pub(super) fn is_rtt_jump_persistent(&self) -> bool {
137 match &self.detector {
138 ActiveRttJumpDetector::Disabled => false,
139 ActiveRttJumpDetector::GlobalMin(detector) =>
140 detector.is_rtt_jump_persistent(),
141 ActiveRttJumpDetector::Hmm(detector) =>
142 detector.is_rtt_jump_persistent(),
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use std::time::Duration;
150 use std::time::Instant;
151
152 use crate::recovery::gcongestion::BbrRttJumpDetector;
153
154 use super::*;
155
156 const RTT_JUMP: Duration = Duration::from_millis(151);
157
158 fn ms(millis: u64) -> Duration {
159 Duration::from_millis(millis)
160 }
161
162 fn sample(
163 detector: &mut RttJumpDetector, base: Instant, offset: Duration,
164 rtt: Duration,
165 ) {
166 detector.on_rtt_sample(rtt, base + offset, true);
167 }
168
169 #[test]
170 fn disabled_detector_does_not_store_active_behavior() {
171 let mut detector = RttJumpDetector::new(BbrRttJumpDetector::Disabled);
172 let base = Instant::now();
173
174 for pkt in 1..20 {
175 sample(&mut detector, base, ms(pkt * 100), RTT_JUMP);
176 }
177
178 assert!(!detector.is_rtt_jump_active());
179 assert!(!detector.is_rtt_jump_persistent());
180 assert_eq!(detector.rtt_persistent_jump_count(), 0);
181 assert_eq!(detector.last_persistent_jump_time(), None);
182 }
183}