use crate::{
counter::{Counter, Saturating},
recovery::{
bandwidth::Bandwidth, congestion_controller::Publisher, RttEstimator, MAX_BURST_PACKETS,
},
time::{Duration, Timestamp},
};
use num_rational::Ratio;
const N: Ratio<u64> = Ratio::new_raw(5, 4);
const SLOW_START_N: Ratio<u64> = Ratio::new_raw(2, 1);
pub const INITIAL_INTERVAL: Duration = Duration::from_millis(0);
pub const MINIMUM_PACING_RTT: Duration = Duration::from_millis(2);
#[derive(Clone, Debug, Default)]
pub struct Pacer {
capacity: Counter<u32, Saturating>,
next_packet_departure_time: Option<Timestamp>,
}
impl Pacer {
#[inline]
pub fn on_packet_sent<Pub: Publisher>(
&mut self,
now: Timestamp,
bytes_sent: usize,
rtt_estimator: &RttEstimator,
congestion_window: u32,
max_datagram_size: u16,
slow_start: bool,
publisher: &mut Pub,
) {
if rtt_estimator.smoothed_rtt() < MINIMUM_PACING_RTT {
return;
}
if self.capacity == 0 {
if let Some(next_packet_departure_time) = self.next_packet_departure_time {
let interval = Self::interval(
rtt_estimator.smoothed_rtt(),
congestion_window,
max_datagram_size,
slow_start,
publisher,
);
self.next_packet_departure_time =
Some((next_packet_departure_time + interval).max(now));
} else {
self.next_packet_departure_time = Some(now + INITIAL_INTERVAL);
}
self.capacity = Counter::new(MAX_BURST_PACKETS * max_datagram_size as u32);
}
self.capacity -= bytes_sent as u32;
}
pub fn earliest_departure_time(&self) -> Option<Timestamp> {
self.next_packet_departure_time
}
#[inline]
fn interval<Pub: Publisher>(
rtt: Duration,
congestion_window: u32,
max_datagram_size: u16,
slow_start: bool,
publisher: &mut Pub,
) -> Duration {
debug_assert_ne!(congestion_window, 0);
let n = if slow_start { SLOW_START_N } else { N };
let pacing_rate = Bandwidth::new(congestion_window as u64, rtt) * n;
let packet_size = MAX_BURST_PACKETS * max_datagram_size as u32;
publisher.on_pacing_rate_updated(pacing_rate, packet_size, n);
packet_size as u64 / pacing_rate
}
}
#[cfg(test)]
mod tests;