1use std::cmp;
28
29use std::time::Duration;
30use std::time::Instant;
31
32use std::collections::VecDeque;
33
34use super::RecoveryConfig;
35use super::Sent;
36
37use crate::packet::Epoch;
38use crate::ranges::RangeSet;
39use crate::recovery::Bandwidth;
40use crate::recovery::HandshakeStatus;
41use crate::recovery::OnLossDetectionTimeoutOutcome;
42use crate::recovery::RecoveryOps;
43use crate::recovery::StartupExit;
44use crate::Error;
45use crate::Result;
46
47#[cfg(feature = "qlog")]
48use crate::recovery::QlogMetrics;
49
50use crate::frame;
51
52#[cfg(feature = "qlog")]
53use qlog::events::EventData;
54
55use super::Congestion;
56use crate::recovery::bytes_in_flight::BytesInFlight;
57use crate::recovery::rtt::RttStats;
58use crate::recovery::LossDetectionTimer;
59use crate::recovery::OnAckReceivedOutcome;
60use crate::recovery::ReleaseDecision;
61use crate::recovery::ReleaseTime;
62use crate::recovery::GRANULARITY;
63use crate::recovery::INITIAL_PACKET_THRESHOLD;
64use crate::recovery::INITIAL_TIME_THRESHOLD;
65use crate::recovery::MAX_OUTSTANDING_NON_ACK_ELICITING;
66use crate::recovery::MAX_PACKET_THRESHOLD;
67use crate::recovery::MAX_PTO_PROBES_COUNT;
68use crate::recovery::PACKET_REORDER_TIME_THRESHOLD;
69
70#[derive(Default)]
71struct RecoveryEpoch {
72 time_of_last_ack_eliciting_packet: Option<Instant>,
74
75 largest_acked_packet: Option<u64>,
78
79 loss_time: Option<Instant>,
82
83 sent_packets: VecDeque<Sent>,
86
87 loss_probes: usize,
88 in_flight_count: usize,
89
90 acked_frames: Vec<frame::Frame>,
91
92 lost_frames_ack: Vec<frame::Frame>,
96 lost_frames_pto: Vec<frame::Frame>,
97
98 #[cfg(test)]
100 test_largest_sent_pkt_num_on_path: Option<u64>,
101}
102
103struct AckedDetectionResult {
104 acked_bytes: usize,
105 spurious_losses: usize,
106 spurious_pkt_thresh: Option<u64>,
107 has_ack_eliciting: bool,
108 has_in_flight_spurious_loss: bool,
109}
110
111struct LossDetectionResult {
112 largest_lost_pkt: Option<Sent>,
113 lost_packets: usize,
114 lost_bytes: usize,
115 pmtud_lost_bytes: usize,
116}
117
118impl RecoveryEpoch {
119 fn detect_and_remove_acked_packets(
121 &mut self, now: Instant, peer_sent_ack_ranges: &RangeSet,
122 newly_acked: &mut Vec<Acked>, rtt_stats: &RttStats, skip_pn: Option<u64>,
123 trace_id: &str,
124 ) -> Result<AckedDetectionResult> {
125 newly_acked.clear();
126
127 let mut acked_bytes = 0;
128 let mut spurious_losses = 0;
129 let mut spurious_pkt_thresh = None;
130 let mut has_ack_eliciting = false;
131 let mut has_in_flight_spurious_loss = false;
132
133 let largest_ack_received = peer_sent_ack_ranges
134 .last()
135 .expect("ACK frames should always have at least one ack range");
136 let largest_acked = self
137 .largest_acked_packet
138 .unwrap_or(0)
139 .max(largest_ack_received);
140
141 for peer_sent_range in peer_sent_ack_ranges.iter() {
142 if skip_pn.is_some_and(|skip_pn| peer_sent_range.contains(&skip_pn)) {
143 return Err(Error::OptimisticAckDetected);
148 }
149
150 let start = if self
153 .sent_packets
154 .front()
155 .filter(|e| e.pkt_num >= peer_sent_range.start)
156 .is_some()
157 {
158 0
160 } else {
161 self.sent_packets
162 .binary_search_by_key(&peer_sent_range.start, |p| p.pkt_num)
163 .unwrap_or_else(|e| e)
164 };
165
166 for unacked in self.sent_packets.range_mut(start..) {
167 if unacked.pkt_num >= peer_sent_range.end {
168 break;
169 }
170
171 if unacked.time_acked.is_some() {
172 } else if unacked.time_lost.is_some() {
174 spurious_losses += 1;
176 spurious_pkt_thresh
177 .get_or_insert(largest_acked - unacked.pkt_num + 1);
178 unacked.time_acked = Some(now);
179
180 if unacked.in_flight {
181 has_in_flight_spurious_loss = true;
182 }
183 } else {
184 if unacked.in_flight {
185 self.in_flight_count -= 1;
186 acked_bytes += unacked.size;
187 }
188
189 newly_acked.push(Acked {
190 pkt_num: unacked.pkt_num,
191 time_sent: unacked.time_sent,
192 size: unacked.size,
193
194 rtt: now.saturating_duration_since(unacked.time_sent),
195 delivered: unacked.delivered,
196 delivered_time: unacked.delivered_time,
197 first_sent_time: unacked.first_sent_time,
198 is_app_limited: unacked.is_app_limited,
199 });
200
201 trace!("{} packet newly acked {}", trace_id, unacked.pkt_num);
202
203 self.acked_frames
204 .extend(std::mem::take(&mut unacked.frames));
205
206 has_ack_eliciting |= unacked.ack_eliciting;
207 unacked.time_acked = Some(now);
208 }
209 }
210 }
211
212 self.drain_acked_and_lost_packets(now - rtt_stats.rtt());
213
214 Ok(AckedDetectionResult {
215 acked_bytes,
216 spurious_losses,
217 spurious_pkt_thresh,
218 has_ack_eliciting,
219 has_in_flight_spurious_loss,
220 })
221 }
222
223 fn detect_lost_packets(
224 &mut self, loss_delay: Duration, pkt_thresh: u64, now: Instant,
225 trace_id: &str, epoch: Epoch,
226 ) -> LossDetectionResult {
227 self.loss_time = None;
228
229 let loss_delay = cmp::max(loss_delay, GRANULARITY);
231 let largest_acked = self.largest_acked_packet.unwrap_or(0);
232
233 let lost_send_time = now.checked_sub(loss_delay).unwrap();
235
236 let mut lost_packets = 0;
237 let mut lost_bytes = 0;
238 let mut pmtud_lost_bytes = 0;
239
240 let mut largest_lost_pkt = None;
241
242 let unacked_iter = self.sent_packets
243 .iter_mut()
244 .take_while(|p| p.pkt_num <= largest_acked)
246 .filter(|p| p.time_acked.is_none() && p.time_lost.is_none());
248
249 for unacked in unacked_iter {
250 if unacked.time_sent <= lost_send_time ||
252 largest_acked >= unacked.pkt_num + pkt_thresh
253 {
254 self.lost_frames_ack.extend(unacked.frames.drain(..));
255
256 unacked.time_lost = Some(now);
257
258 if unacked.is_pmtud_probe {
259 pmtud_lost_bytes += unacked.size;
260 self.in_flight_count -= 1;
261
262 continue;
264 }
265
266 if unacked.in_flight {
267 lost_bytes += unacked.size;
268
269 largest_lost_pkt = Some(unacked.clone());
272
273 self.in_flight_count -= 1;
274
275 trace!(
276 "{} packet {} lost on epoch {}",
277 trace_id,
278 unacked.pkt_num,
279 epoch
280 );
281 }
282
283 lost_packets += 1;
284 } else {
285 let loss_time = match self.loss_time {
286 None => unacked.time_sent + loss_delay,
287
288 Some(loss_time) =>
289 cmp::min(loss_time, unacked.time_sent + loss_delay),
290 };
291
292 self.loss_time = Some(loss_time);
293 break;
294 }
295 }
296
297 LossDetectionResult {
298 largest_lost_pkt,
299 lost_packets,
300 lost_bytes,
301 pmtud_lost_bytes,
302 }
303 }
304
305 fn drain_acked_and_lost_packets(&mut self, loss_thresh: Instant) {
306 while let Some(pkt) = self.sent_packets.front() {
315 if let Some(time_lost) = pkt.time_lost {
316 if time_lost > loss_thresh {
317 break;
318 }
319 }
320
321 if pkt.time_acked.is_none() && pkt.time_lost.is_none() {
322 break;
323 }
324
325 self.sent_packets.pop_front();
326 }
327 }
328
329 fn next_lost_frame(&mut self) -> Option<frame::Frame> {
332 self.lost_frames_ack
333 .pop()
334 .or_else(|| self.lost_frames_pto.pop())
335 }
336
337 fn has_lost_frames(&self) -> bool {
339 !self.lost_frames_ack.is_empty() || !self.lost_frames_pto.is_empty()
340 }
341
342 #[cfg(test)]
344 fn lost_frames_count(&self) -> usize {
345 self.lost_frames_ack.len() + self.lost_frames_pto.len()
346 }
347
348 fn clear_lost_frames(&mut self) {
350 self.lost_frames_ack.clear();
351 self.lost_frames_pto.clear();
352 }
353}
354
355pub struct LegacyRecovery {
356 epochs: [RecoveryEpoch; Epoch::count()],
357
358 loss_timer: LossDetectionTimer,
359
360 pto_count: u32,
361
362 rtt_stats: RttStats,
363
364 lost_spurious_count: usize,
365
366 pkt_thresh: u64,
367
368 time_thresh: f64,
369
370 bytes_in_flight: BytesInFlight,
371
372 bytes_sent: usize,
373
374 bytes_lost: u64,
375
376 pub max_datagram_size: usize,
377
378 #[cfg(feature = "qlog")]
379 qlog_metrics: QlogMetrics,
380
381 #[cfg(feature = "qlog")]
382 qlog_prev_cc_state: &'static str,
383
384 outstanding_non_ack_eliciting: usize,
386
387 pub congestion: Congestion,
388
389 newly_acked: Vec<Acked>,
391}
392
393impl LegacyRecovery {
394 pub fn new_with_config(recovery_config: &RecoveryConfig) -> Self {
395 Self {
396 epochs: Default::default(),
397
398 loss_timer: Default::default(),
399
400 pto_count: 0,
401
402 rtt_stats: RttStats::new(
403 recovery_config.initial_rtt,
404 recovery_config.max_ack_delay,
405 ),
406
407 lost_spurious_count: 0,
408
409 pkt_thresh: INITIAL_PACKET_THRESHOLD,
410
411 time_thresh: INITIAL_TIME_THRESHOLD,
412
413 bytes_in_flight: Default::default(),
414
415 bytes_sent: 0,
416
417 bytes_lost: 0,
418
419 max_datagram_size: recovery_config.max_send_udp_payload_size,
420
421 #[cfg(feature = "qlog")]
422 qlog_metrics: QlogMetrics::default(),
423
424 #[cfg(feature = "qlog")]
425 qlog_prev_cc_state: "",
426
427 outstanding_non_ack_eliciting: 0,
428
429 congestion: Congestion::from_config(recovery_config),
430
431 newly_acked: Vec::new(),
432 }
433 }
434
435 #[cfg(test)]
436 pub fn new(config: &crate::Config) -> Self {
437 Self::new_with_config(&RecoveryConfig::from_config(config))
438 }
439
440 fn loss_time_and_space(&self) -> (Option<Instant>, Epoch) {
441 let mut epoch = Epoch::Initial;
442 let mut time = self.epochs[epoch].loss_time;
443
444 for e in [Epoch::Handshake, Epoch::Application] {
446 let new_time = self.epochs[e].loss_time;
447 if time.is_none() || new_time < time {
448 time = new_time;
449 epoch = e;
450 }
451 }
452
453 (time, epoch)
454 }
455
456 fn pto_time_and_space(
457 &self, handshake_status: HandshakeStatus, now: Instant,
458 ) -> (Option<Instant>, Epoch) {
459 let mut duration = self.pto() * 2_u32.saturating_pow(self.pto_count);
460
461 if self.bytes_in_flight.is_zero() {
463 if handshake_status.has_handshake_keys {
464 return (Some(now + duration), Epoch::Handshake);
465 } else {
466 return (Some(now + duration), Epoch::Initial);
467 }
468 }
469
470 let mut pto_timeout = None;
471 let mut pto_space = Epoch::Initial;
472
473 for e in [Epoch::Initial, Epoch::Handshake, Epoch::Application] {
475 let epoch = &self.epochs[e];
476 if epoch.in_flight_count == 0 {
477 continue;
478 }
479
480 if e == Epoch::Application {
481 if !handshake_status.completed {
483 return (pto_timeout, pto_space);
484 }
485
486 duration += self.rtt_stats.max_ack_delay *
488 2_u32.saturating_pow(self.pto_count);
489 }
490
491 let new_time = epoch
492 .time_of_last_ack_eliciting_packet
493 .map(|t| t + duration);
494
495 if pto_timeout.is_none() || new_time < pto_timeout {
496 pto_timeout = new_time;
497 pto_space = e;
498 }
499 }
500
501 (pto_timeout, pto_space)
502 }
503
504 fn set_loss_detection_timer(
505 &mut self, handshake_status: HandshakeStatus, now: Instant,
506 ) {
507 let (earliest_loss_time, _) = self.loss_time_and_space();
508
509 if let Some(to) = earliest_loss_time {
510 self.loss_timer.update(to);
512 return;
513 }
514
515 if self.bytes_in_flight.is_zero() &&
516 handshake_status.peer_verified_address
517 {
518 self.loss_timer.clear();
519 return;
520 }
521
522 if let (Some(timeout), _) = self.pto_time_and_space(handshake_status, now)
524 {
525 self.loss_timer.update(timeout);
526 } else {
527 self.loss_timer.clear();
528 }
529 }
530
531 fn detect_lost_packets(
532 &mut self, epoch: Epoch, now: Instant, trace_id: &str,
533 ) -> (usize, usize) {
534 let loss_delay = cmp::max(self.rtt_stats.latest_rtt, self.rtt())
535 .mul_f64(self.time_thresh);
536
537 let loss = self.epochs[epoch].detect_lost_packets(
538 loss_delay,
539 self.pkt_thresh,
540 now,
541 trace_id,
542 epoch,
543 );
544
545 if let Some(pkt) = loss.largest_lost_pkt {
546 if !self.congestion.in_congestion_recovery(pkt.time_sent) {
547 (self.congestion.cc_ops.checkpoint)(&mut self.congestion);
548 }
549
550 (self.congestion.cc_ops.congestion_event)(
551 &mut self.congestion,
552 self.bytes_in_flight.get(),
553 loss.lost_bytes,
554 &pkt,
555 now,
556 );
557
558 self.bytes_in_flight
559 .saturating_subtract(loss.lost_bytes, now);
560 };
561
562 self.bytes_in_flight
563 .saturating_subtract(loss.pmtud_lost_bytes, now);
564
565 self.epochs[epoch]
566 .drain_acked_and_lost_packets(now - self.rtt_stats.rtt());
567
568 self.congestion.lost_count += loss.lost_packets;
569
570 (loss.lost_packets, loss.lost_bytes)
571 }
572}
573
574impl RecoveryOps for LegacyRecovery {
575 fn should_elicit_ack(&self, epoch: Epoch) -> bool {
578 self.epochs[epoch].loss_probes > 0 ||
579 self.outstanding_non_ack_eliciting >=
580 MAX_OUTSTANDING_NON_ACK_ELICITING
581 }
582
583 fn next_acked_frame(&mut self, epoch: Epoch) -> Option<frame::Frame> {
584 self.epochs[epoch].acked_frames.pop()
585 }
586
587 fn next_lost_frame(&mut self, epoch: Epoch) -> Option<frame::Frame> {
588 self.epochs[epoch].next_lost_frame()
589 }
590
591 fn get_largest_acked_on_epoch(&self, epoch: Epoch) -> Option<u64> {
592 self.epochs[epoch].largest_acked_packet
593 }
594
595 fn has_lost_frames(&self, epoch: Epoch) -> bool {
596 self.epochs[epoch].has_lost_frames()
597 }
598
599 fn loss_probes(&self, epoch: Epoch) -> usize {
600 self.epochs[epoch].loss_probes
601 }
602
603 #[cfg(test)]
604 fn inc_loss_probes(&mut self, epoch: Epoch) {
605 self.epochs[epoch].loss_probes += 1;
606 }
607
608 #[cfg(test)]
609 fn lost_frames_count(&self, epoch: Epoch) -> usize {
610 self.epochs[epoch].lost_frames_count()
611 }
612
613 fn ping_sent(&mut self, epoch: Epoch) {
614 self.epochs[epoch].loss_probes =
615 self.epochs[epoch].loss_probes.saturating_sub(1);
616 }
617
618 fn on_packet_sent(
619 &mut self, mut pkt: Sent, epoch: Epoch,
620 handshake_status: HandshakeStatus, now: Instant, trace_id: &str,
621 ) {
622 let ack_eliciting = pkt.ack_eliciting;
623 let in_flight = pkt.in_flight;
624 let sent_bytes = pkt.size;
625
626 if ack_eliciting {
627 self.outstanding_non_ack_eliciting = 0;
628 } else {
629 self.outstanding_non_ack_eliciting += 1;
630 }
631
632 if in_flight && ack_eliciting {
633 self.epochs[epoch].time_of_last_ack_eliciting_packet = Some(now);
634 }
635
636 self.congestion.on_packet_sent(
637 self.bytes_in_flight.get(),
638 sent_bytes,
639 now,
640 &mut pkt,
641 self.bytes_lost,
642 in_flight,
643 );
644
645 if in_flight {
646 self.epochs[epoch].in_flight_count += 1;
647 self.bytes_in_flight.add(sent_bytes, now);
648
649 self.set_loss_detection_timer(handshake_status, now);
650 }
651
652 self.bytes_sent += sent_bytes;
653
654 #[cfg(test)]
655 {
656 self.epochs[epoch].test_largest_sent_pkt_num_on_path = self.epochs
657 [epoch]
658 .test_largest_sent_pkt_num_on_path
659 .max(Some(pkt.pkt_num));
660 }
661
662 self.epochs[epoch].sent_packets.push_back(pkt);
663
664 trace!("{trace_id} {self:?}");
665 }
666
667 fn get_packet_send_time(&self, now: Instant) -> Instant {
668 now
669 }
670
671 fn on_ack_received(
673 &mut self, peer_sent_ack_ranges: &RangeSet, ack_delay: u64, epoch: Epoch,
674 handshake_status: HandshakeStatus, now: Instant, skip_pn: Option<u64>,
675 trace_id: &str,
676 ) -> Result<OnAckReceivedOutcome> {
677 let AckedDetectionResult {
678 acked_bytes,
679 spurious_losses,
680 spurious_pkt_thresh,
681 has_ack_eliciting,
682 has_in_flight_spurious_loss,
683 } = self.epochs[epoch].detect_and_remove_acked_packets(
684 now,
685 peer_sent_ack_ranges,
686 &mut self.newly_acked,
687 &self.rtt_stats,
688 skip_pn,
689 trace_id,
690 )?;
691
692 self.lost_spurious_count += spurious_losses;
693 if let Some(thresh) = spurious_pkt_thresh {
694 self.pkt_thresh =
695 self.pkt_thresh.max(thresh.min(MAX_PACKET_THRESHOLD));
696 self.time_thresh = PACKET_REORDER_TIME_THRESHOLD;
697 }
698
699 if has_in_flight_spurious_loss {
701 (self.congestion.cc_ops.rollback)(&mut self.congestion);
702 }
703
704 if self.newly_acked.is_empty() {
705 return Ok(OnAckReceivedOutcome::default());
706 }
707
708 let largest_newly_acked = self.newly_acked.last().unwrap();
709
710 let largest_acked_pkt_num = self.epochs[epoch]
713 .largest_acked_packet
714 .unwrap_or(0)
715 .max(largest_newly_acked.pkt_num);
716 self.epochs[epoch].largest_acked_packet = Some(largest_acked_pkt_num);
717
718 if largest_newly_acked.pkt_num == largest_acked_pkt_num &&
720 has_ack_eliciting
721 {
722 let latest_rtt = now - largest_newly_acked.time_sent;
723 self.rtt_stats.update_rtt(
724 latest_rtt,
725 Duration::from_micros(ack_delay),
726 now,
727 handshake_status.completed,
728 );
729 }
730
731 let (lost_packets, lost_bytes) =
734 self.detect_lost_packets(epoch, now, trace_id);
735
736 self.congestion.on_packets_acked(
737 self.bytes_in_flight.get(),
738 &mut self.newly_acked,
739 &self.rtt_stats,
740 now,
741 );
742
743 self.bytes_in_flight.saturating_subtract(acked_bytes, now);
744
745 self.pto_count = 0;
746
747 self.set_loss_detection_timer(handshake_status, now);
748
749 self.epochs[epoch]
750 .drain_acked_and_lost_packets(now - self.rtt_stats.rtt());
751
752 Ok(OnAckReceivedOutcome {
753 lost_packets,
754 lost_bytes,
755 acked_bytes,
756 spurious_losses,
757 })
758 }
759
760 fn on_loss_detection_timeout(
761 &mut self, handshake_status: HandshakeStatus, now: Instant,
762 trace_id: &str,
763 ) -> OnLossDetectionTimeoutOutcome {
764 let (earliest_loss_time, epoch) = self.loss_time_and_space();
765
766 if earliest_loss_time.is_some() {
767 let (lost_packets, lost_bytes) =
769 self.detect_lost_packets(epoch, now, trace_id);
770
771 self.set_loss_detection_timer(handshake_status, now);
772
773 trace!("{trace_id} {self:?}");
774 return OnLossDetectionTimeoutOutcome {
775 lost_packets,
776 lost_bytes,
777 };
778 }
779
780 let epoch = if self.bytes_in_flight.get() > 0 {
781 let (_, e) = self.pto_time_and_space(handshake_status, now);
784
785 e
786 } else {
787 if handshake_status.has_handshake_keys {
791 Epoch::Handshake
792 } else {
793 Epoch::Initial
794 }
795 };
796
797 self.pto_count += 1;
798
799 let epoch = &mut self.epochs[epoch];
800
801 epoch.loss_probes =
802 cmp::min(self.pto_count as usize, MAX_PTO_PROBES_COUNT);
803
804 let sent_packets_iter_limit = if !epoch.lost_frames_pto.is_empty() {
805 0
808 } else {
809 usize::MAX
810 };
811
812 let unacked_iter = epoch.sent_packets
813 .iter()
814 .take(sent_packets_iter_limit)
815 .filter(|p| p.has_data && p.time_acked.is_none() && p.time_lost.is_none())
818 .take(epoch.loss_probes);
821
822 for unacked in unacked_iter {
830 epoch.lost_frames_pto.extend_from_slice(&unacked.frames);
831 }
832
833 self.set_loss_detection_timer(handshake_status, now);
834
835 trace!("{trace_id} {self:?}");
836
837 OnLossDetectionTimeoutOutcome {
838 lost_packets: 0,
839 lost_bytes: 0,
840 }
841 }
842
843 fn on_pkt_num_space_discarded(
844 &mut self, epoch: Epoch, handshake_status: HandshakeStatus, now: Instant,
845 ) {
846 let epoch = &mut self.epochs[epoch];
847
848 let unacked_bytes = epoch
849 .sent_packets
850 .iter()
851 .filter(|p| {
852 p.in_flight && p.time_acked.is_none() && p.time_lost.is_none()
853 })
854 .fold(0, |acc, p| acc + p.size);
855
856 self.bytes_in_flight.saturating_subtract(unacked_bytes, now);
857
858 epoch.sent_packets.clear();
859 epoch.clear_lost_frames();
860 epoch.acked_frames.clear();
861
862 epoch.time_of_last_ack_eliciting_packet = None;
863 epoch.loss_time = None;
864 epoch.loss_probes = 0;
865 epoch.in_flight_count = 0;
866
867 self.set_loss_detection_timer(handshake_status, now);
868 }
869
870 fn on_path_change(
871 &mut self, epoch: Epoch, now: Instant, trace_id: &str,
872 ) -> (usize, usize) {
873 self.detect_lost_packets(epoch, now, trace_id)
875 }
876
877 fn loss_detection_timer(&self) -> Option<Instant> {
878 self.loss_timer.time
879 }
880
881 fn cwnd(&self) -> usize {
882 self.congestion.congestion_window()
883 }
884
885 fn cwnd_available(&self) -> usize {
886 if self.epochs.iter().any(|e| e.loss_probes > 0) {
888 return usize::MAX;
889 }
890
891 self.cwnd().saturating_sub(self.bytes_in_flight.get()) +
893 self.congestion.prr.snd_cnt
894 }
895
896 fn rtt(&self) -> Duration {
897 self.rtt_stats.rtt()
898 }
899
900 fn min_rtt(&self) -> Option<Duration> {
901 self.rtt_stats.min_rtt()
902 }
903
904 fn max_rtt(&self) -> Option<Duration> {
905 self.rtt_stats.max_rtt()
906 }
907
908 fn rttvar(&self) -> Duration {
909 self.rtt_stats.rttvar
910 }
911
912 fn pto(&self) -> Duration {
913 self.rtt() + cmp::max(self.rtt_stats.rttvar * 4, GRANULARITY)
914 }
915
916 fn delivery_rate(&self) -> Bandwidth {
918 self.congestion.delivery_rate()
919 }
920
921 fn max_bandwidth(&self) -> Option<Bandwidth> {
922 None
924 }
925
926 fn rtt_persistent_jump_count(&self) -> u64 {
927 0
934 }
935
936 fn startup_exit(&self) -> Option<StartupExit> {
938 self.congestion.ssthresh.startup_exit()
939 }
940
941 fn max_datagram_size(&self) -> usize {
942 self.max_datagram_size
943 }
944
945 fn pmtud_update_max_datagram_size(&mut self, new_max_datagram_size: usize) {
946 if self.cwnd() ==
949 self.max_datagram_size *
950 self.congestion.initial_congestion_window_packets
951 {
952 self.congestion.congestion_window = new_max_datagram_size *
953 self.congestion.initial_congestion_window_packets;
954 }
955
956 self.max_datagram_size = new_max_datagram_size;
957 }
958
959 fn update_max_datagram_size(&mut self, new_max_datagram_size: usize) {
960 self.pmtud_update_max_datagram_size(
961 self.max_datagram_size.min(new_max_datagram_size),
962 )
963 }
964
965 #[cfg(test)]
966 fn sent_packets_len(&self, epoch: Epoch) -> usize {
967 self.epochs[epoch].sent_packets.len()
968 }
969
970 #[cfg(test)]
971 fn in_flight_count(&self, epoch: Epoch) -> usize {
972 self.epochs[epoch].in_flight_count
973 }
974
975 fn bytes_in_flight(&self) -> usize {
976 self.bytes_in_flight.get()
977 }
978
979 fn bytes_in_flight_duration(&self) -> Duration {
980 self.bytes_in_flight.get_duration()
981 }
982
983 #[cfg(test)]
984 fn pacing_rate(&self) -> u64 {
985 0
986 }
987
988 #[cfg(test)]
989 fn pto_count(&self) -> u32 {
990 self.pto_count
991 }
992
993 #[cfg(test)]
994 fn pkt_thresh(&self) -> Option<u64> {
995 Some(self.pkt_thresh)
996 }
997
998 #[cfg(test)]
999 fn time_thresh(&self) -> f64 {
1000 self.time_thresh
1001 }
1002
1003 #[cfg(test)]
1004 fn lost_spurious_count(&self) -> usize {
1005 self.lost_spurious_count
1006 }
1007
1008 #[cfg(test)]
1009 fn detect_lost_packets_for_test(
1010 &mut self, epoch: Epoch, now: Instant,
1011 ) -> (usize, usize) {
1012 self.detect_lost_packets(epoch, now, "")
1013 }
1014
1015 fn on_app_limited(&mut self) {
1017 }
1020
1021 #[cfg(test)]
1022 fn largest_sent_pkt_num_on_path(&self, epoch: Epoch) -> Option<u64> {
1023 self.epochs[epoch].test_largest_sent_pkt_num_on_path
1024 }
1025
1026 #[cfg(any(test, feature = "qlog"))]
1027 fn app_limited(&self) -> bool {
1028 self.congestion.app_limited
1029 }
1030
1031 fn update_app_limited(&mut self, v: bool) {
1032 self.congestion.update_app_limited(v);
1033 }
1034
1035 fn delivery_rate_update_app_limited(&mut self, v: bool) {
1037 self.congestion.delivery_rate.update_app_limited(v);
1038 }
1039
1040 fn update_max_ack_delay(&mut self, max_ack_delay: Duration) {
1042 self.rtt_stats.max_ack_delay = max_ack_delay;
1043 }
1044
1045 #[cfg(feature = "qlog")]
1046 fn state_str(&self, now: Instant) -> &'static str {
1047 (self.congestion.cc_ops.state_str)(&self.congestion, now)
1048 }
1049
1050 #[cfg(feature = "qlog")]
1051 fn get_updated_qlog_event_data(&mut self) -> Option<EventData> {
1052 let qlog_metrics = QlogMetrics {
1053 min_rtt: *self.rtt_stats.min_rtt,
1054 smoothed_rtt: self.rtt(),
1055 latest_rtt: self.rtt_stats.latest_rtt,
1056 rttvar: self.rtt_stats.rttvar,
1057 cwnd: self.cwnd() as u64,
1058 bytes_in_flight: self.bytes_in_flight.get() as u64,
1059 ssthresh: Some(self.congestion.ssthresh.get() as u64),
1060 lost_packets: Some(self.congestion.lost_count as u64),
1061 lost_bytes: Some(self.bytes_lost),
1062 pto_count: Some(self.pto_count),
1063 app_limited: Some(self.app_limited()),
1064 ..Default::default()
1065 };
1066
1067 self.qlog_metrics.maybe_update(qlog_metrics)
1068 }
1069
1070 #[cfg(feature = "qlog")]
1071 fn get_updated_qlog_cc_state(
1072 &mut self, now: Instant,
1073 ) -> Option<&'static str> {
1074 let cc_state = self.state_str(now);
1075 if cc_state != self.qlog_prev_cc_state {
1076 self.qlog_prev_cc_state = cc_state;
1077 Some(cc_state)
1078 } else {
1079 None
1080 }
1081 }
1082
1083 fn send_quantum(&self) -> usize {
1084 self.congestion.send_quantum()
1085 }
1086
1087 fn get_next_release_time(&self) -> ReleaseDecision {
1088 ReleaseDecision {
1089 time: ReleaseTime::Immediate,
1090 allow_burst: false,
1091 }
1092 }
1093
1094 fn gcongestion_enabled(&self) -> bool {
1095 false
1096 }
1097
1098 fn lost_count(&self) -> usize {
1099 self.congestion.lost_count
1100 }
1101
1102 fn bytes_lost(&self) -> u64 {
1103 self.bytes_lost
1104 }
1105}
1106
1107impl std::fmt::Debug for LegacyRecovery {
1108 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1109 write!(f, "timer={:?} ", self.loss_timer)?;
1110 write!(f, "latest_rtt={:?} ", self.rtt_stats.latest_rtt)?;
1111 write!(f, "srtt={:?} ", self.rtt_stats.smoothed_rtt)?;
1112 write!(f, "min_rtt={:?} ", *self.rtt_stats.min_rtt)?;
1113 write!(f, "rttvar={:?} ", self.rtt_stats.rttvar)?;
1114 write!(f, "cwnd={} ", self.cwnd())?;
1115 write!(f, "ssthresh={} ", self.congestion.ssthresh.get())?;
1116 write!(f, "bytes_in_flight={} ", self.bytes_in_flight.get())?;
1117 write!(f, "app_limited={} ", self.congestion.app_limited)?;
1118 write!(
1119 f,
1120 "congestion_recovery_start_time={:?} ",
1121 self.congestion.congestion_recovery_start_time
1122 )?;
1123 write!(f, "{:?} ", self.congestion.delivery_rate)?;
1124
1125 if self.congestion.hystart.enabled() {
1126 write!(f, "hystart={:?} ", self.congestion.hystart)?;
1127 }
1128
1129 (self.congestion.cc_ops.debug_fmt)(&self.congestion, f)?;
1131
1132 Ok(())
1133 }
1134}
1135
1136#[derive(Clone)]
1137pub struct Acked {
1138 pub pkt_num: u64,
1139
1140 pub time_sent: Instant,
1141
1142 pub size: usize,
1143
1144 pub rtt: Duration,
1145
1146 pub delivered: usize,
1147
1148 pub delivered_time: Instant,
1149
1150 pub first_sent_time: Instant,
1151
1152 pub is_app_limited: bool,
1153}
1154
1155#[cfg(test)]
1156mod tests {
1157 use super::*;
1158 use crate::recovery::HandshakeStatus;
1159 use crate::recovery::RecoveryConfig;
1160 use std::time::Instant;
1161
1162 #[test]
1163 fn test_high_pto_count_no_panic() {
1164 let config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
1165 let recovery_config = RecoveryConfig::from_config(&config);
1166 let mut r = LegacyRecovery::new_with_config(&recovery_config);
1167
1168 r.pto_count = 99999;
1169
1170 let handshake_status = HandshakeStatus {
1171 completed: true,
1172 has_handshake_keys: true,
1173 peer_verified_address: true,
1174 };
1175 let now = Instant::now();
1176
1177 let _ = r.pto_time_and_space(handshake_status, now);
1178 }
1179}