quiche/recovery/congestion/
reno.rsuse std::cmp;
use std::time::Instant;
use crate::recovery;
use crate::recovery::rtt::RttStats;
use crate::recovery::Acked;
use crate::recovery::Sent;
use super::Congestion;
use super::CongestionControlOps;
pub(crate) static RENO: CongestionControlOps = CongestionControlOps {
on_init,
on_packet_sent,
on_packets_acked,
congestion_event,
checkpoint,
rollback,
has_custom_pacing,
debug_fmt,
};
pub fn on_init(_r: &mut Congestion) {}
pub fn on_packet_sent(
_r: &mut Congestion, _sent_bytes: usize, _bytes_in_flight: usize,
_now: Instant,
) {
}
fn on_packets_acked(
r: &mut Congestion, _bytes_in_flight: usize, packets: &mut Vec<Acked>,
now: Instant, rtt_stats: &RttStats,
) {
for pkt in packets.drain(..) {
on_packet_acked(r, &pkt, now, rtt_stats);
}
}
fn on_packet_acked(
r: &mut Congestion, packet: &Acked, now: Instant, rtt_stats: &RttStats,
) {
if r.in_congestion_recovery(packet.time_sent) {
return;
}
if r.app_limited {
return;
}
if r.congestion_window < r.ssthresh {
r.bytes_acked_sl += packet.size;
if r.hystart.in_css() {
r.congestion_window += r.hystart.css_cwnd_inc(r.max_datagram_size);
} else {
r.congestion_window += r.max_datagram_size;
}
if r.hystart.on_packet_acked(packet, rtt_stats.latest_rtt, now) {
r.ssthresh = r.congestion_window;
}
} else {
r.bytes_acked_ca += packet.size;
if r.bytes_acked_ca >= r.congestion_window {
r.bytes_acked_ca -= r.congestion_window;
r.congestion_window += r.max_datagram_size;
}
}
}
fn congestion_event(
r: &mut Congestion, _bytes_in_flight: usize, _lost_bytes: usize,
largest_lost_pkt: &Sent, now: Instant,
) {
let time_sent = largest_lost_pkt.time_sent;
if !r.in_congestion_recovery(time_sent) {
r.congestion_recovery_start_time = Some(now);
r.congestion_window = (r.congestion_window as f64 *
recovery::LOSS_REDUCTION_FACTOR)
as usize;
r.congestion_window = cmp::max(
r.congestion_window,
r.max_datagram_size * recovery::MINIMUM_WINDOW_PACKETS,
);
r.bytes_acked_ca = (r.congestion_window as f64 *
recovery::LOSS_REDUCTION_FACTOR) as usize;
r.ssthresh = r.congestion_window;
if r.hystart.in_css() {
r.hystart.congestion_event();
}
}
}
fn checkpoint(_r: &mut Congestion) {}
fn rollback(_r: &mut Congestion) -> bool {
true
}
fn has_custom_pacing() -> bool {
false
}
fn debug_fmt(_r: &Congestion, _f: &mut std::fmt::Formatter) -> std::fmt::Result {
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::recovery::congestion::test_sender::TestSender;
use crate::recovery::Recovery;
use std::time::Duration;
fn test_sender() -> TestSender {
TestSender::new(recovery::CongestionControlAlgorithm::Reno, false)
}
#[test]
fn reno_init() {
let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::Reno);
let r = Recovery::new(&cfg);
assert!(r.cwnd() > 0);
assert_eq!(r.bytes_in_flight, 0);
}
#[test]
fn reno_slow_start() {
let mut sender = test_sender();
let size = sender.max_datagram_size;
for _ in 0..sender.initial_congestion_window_packets {
sender.send_packet(size);
}
let cwnd_prev = sender.congestion_window;
sender.ack_n_packets(1, size);
assert_eq!(sender.congestion_window, cwnd_prev + size);
}
#[test]
fn reno_slow_start_multi_acks() {
let mut sender = test_sender();
let size = sender.max_datagram_size;
for _ in 0..sender.initial_congestion_window_packets {
sender.send_packet(size);
}
let cwnd_prev = sender.congestion_window;
sender.ack_n_packets(3, size);
assert_eq!(sender.congestion_window, cwnd_prev + size * 3);
}
#[test]
fn reno_congestion_event() {
let mut sender = test_sender();
let size = sender.max_datagram_size;
let prev_cwnd = sender.congestion_window;
sender.send_packet(size);
sender.lose_n_packets(1, size, None);
assert_eq!(prev_cwnd / 2, sender.congestion_window);
}
#[test]
fn reno_congestion_avoidance() {
let mut sender = test_sender();
let size = sender.max_datagram_size;
for _ in 0..14 {
sender.send_packet(size);
}
let prev_cwnd = sender.congestion_window;
sender.lose_n_packets(1, size, None);
let cur_cwnd =
(prev_cwnd as f64 * recovery::LOSS_REDUCTION_FACTOR) as usize;
assert_eq!(sender.congestion_window, cur_cwnd);
let rtt = Duration::from_millis(100);
sender.update_rtt(rtt);
sender.advance_time(2 * rtt);
sender.ack_n_packets(8, size);
assert_eq!(sender.congestion_window, cur_cwnd + size);
}
}