use crate::{
event::builder::SlowStartExitCause,
recovery::{
bbr::{BbrCongestionController, State},
congestion_controller::Publisher,
},
};
use num_rational::Ratio;
pub(crate) const PACING_GAIN: Ratio<u64> = Ratio::new_raw(277, 100);
pub(crate) const CWND_GAIN: Ratio<u64> = Ratio::new_raw(2, 1);
impl BbrCongestionController {
#[inline]
pub(super) fn enter_startup<Pub: Publisher>(&mut self, publisher: &mut Pub) {
self.try_fast_path = false;
self.state.transition_to(State::Startup, publisher);
}
#[inline]
pub(super) fn check_startup_done<Pub: Publisher>(&mut self, publisher: &mut Pub) {
if self.round_counter.round_start() {
self.full_pipe_estimator.on_round_start(
self.bw_estimator.rate_sample(),
self.data_rate_model.max_bw(),
self.ecn_state.is_ce_too_high_in_round(),
);
}
if self.congestion_state.loss_round_start() {
self.full_pipe_estimator.on_loss_round_start(
self.bw_estimator.rate_sample(),
self.congestion_state.loss_bursts_in_round(),
self.max_datagram_size,
&self.app_settings,
)
}
if self.state.is_startup() && self.full_pipe_estimator.filled_pipe() {
publisher.on_slow_start_exited(SlowStartExitCause::Other, self.cwnd);
self.enter_drain(publisher);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
event, path,
path::MINIMUM_MAX_DATAGRAM_SIZE,
recovery::{bandwidth::PacketInfo, bbr::probe_rtt, congestion_controller::PathPublisher},
time::{Clock, NoopClock},
};
#[test]
fn enter_startup() {
let mut bbr = BbrCongestionController::new(MINIMUM_MAX_DATAGRAM_SIZE, Default::default());
let mut publisher = event::testing::Publisher::snapshot();
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
bbr.state = State::ProbeRtt(probe_rtt::State::default());
bbr.enter_startup(&mut publisher);
assert!(bbr.state.is_startup());
assert!(!bbr.try_fast_path);
}
#[test]
fn check_startup_done() {
let mut bbr = BbrCongestionController::new(MINIMUM_MAX_DATAGRAM_SIZE, Default::default());
let mut publisher = event::testing::Publisher::snapshot();
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
bbr.state = State::ProbeRtt(probe_rtt::State::default());
bbr.full_pipe_estimator.set_filled_pipe_for_test(true);
bbr.check_startup_done(&mut publisher);
assert!(bbr.state.is_probing_rtt());
bbr.state = State::Startup;
bbr.full_pipe_estimator.set_filled_pipe_for_test(false);
bbr.check_startup_done(&mut publisher);
assert!(bbr.state.is_startup());
bbr.state = State::Startup;
bbr.full_pipe_estimator.set_filled_pipe_for_test(true);
bbr.check_startup_done(&mut publisher);
assert!(bbr.state.is_drain());
}
#[test]
fn check_startup_done_filled_pipe_on_round_start() {
let mut bbr = BbrCongestionController::new(MINIMUM_MAX_DATAGRAM_SIZE, Default::default());
let mut publisher = event::testing::Publisher::snapshot();
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
let now = NoopClock.get_time();
bbr.ecn_state.on_explicit_congestion(1000);
bbr.ecn_state.on_round_start(
1000 * MINIMUM_MAX_DATAGRAM_SIZE as u64,
MINIMUM_MAX_DATAGRAM_SIZE,
);
assert!(!bbr.round_counter.round_start());
bbr.check_startup_done(&mut publisher);
bbr.check_startup_done(&mut publisher);
assert!(!bbr.full_pipe_estimator.filled_pipe());
assert!(bbr.state.is_startup());
bbr.round_counter.set_round_end(100);
let packet_info = PacketInfo {
delivered_bytes: 100,
delivered_time: now,
lost_bytes: 0,
ecn_ce_count: 0,
first_sent_time: now,
bytes_in_flight: 0,
is_app_limited: false,
};
bbr.round_counter.on_ack(packet_info, 200);
assert!(bbr.round_counter.round_start());
bbr.check_startup_done(&mut publisher);
bbr.check_startup_done(&mut publisher);
assert!(bbr.full_pipe_estimator.filled_pipe());
assert!(bbr.state.is_drain());
}
#[test]
fn check_startup_done_filled_pipe_on_loss_round_start() {
let mut bbr = BbrCongestionController::new(MINIMUM_MAX_DATAGRAM_SIZE, Default::default());
let mut publisher = event::testing::Publisher::snapshot();
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
let now = NoopClock.get_time();
bbr.bw_estimator.on_loss(1000);
bbr.bw_estimator.set_delivered_bytes_for_test(100);
for _ in 0..8 {
bbr.congestion_state.on_packet_lost(100, true);
}
assert!(!bbr.congestion_state.loss_round_start());
bbr.check_startup_done(&mut publisher);
assert!(!bbr.full_pipe_estimator.filled_pipe());
assert!(bbr.state.is_startup());
let packet_info = PacketInfo {
delivered_bytes: 100,
delivered_time: now,
lost_bytes: 0,
ecn_ce_count: 0,
first_sent_time: now,
bytes_in_flight: 0,
is_app_limited: false,
};
bbr.update_latest_signals(packet_info);
assert!(bbr.congestion_state.loss_round_start());
bbr.check_startup_done(&mut publisher);
assert!(bbr.full_pipe_estimator.filled_pipe());
assert!(bbr.state.is_drain());
}
}