use std::time::Duration;
use std::time::Instant;
use crate::minmax::Minmax;
use crate::recovery::GRANULARITY;
pub(crate) const RTT_WINDOW: Duration = Duration::from_secs(300);
pub struct RttStats {
pub(super) latest_rtt: Duration,
max_rtt: Duration,
pub(super) smoothed_rtt: Duration,
pub(super) rttvar: Duration,
pub(super) min_rtt: Minmax<Duration>,
pub(super) max_ack_delay: Duration,
pub(super) has_first_rtt_sample: bool,
}
impl std::fmt::Debug for RttStats {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("RttStats")
.field("lastest_rtt", &self.latest_rtt)
.field("srtt", &self.smoothed_rtt)
.field("minrtt", &*self.min_rtt)
.field("rttvar", &self.rttvar)
.finish()
}
}
impl RttStats {
pub(crate) fn new(initial_rtt: Duration, max_ack_delay: Duration) -> Self {
RttStats {
latest_rtt: Duration::ZERO,
min_rtt: Minmax::new(initial_rtt),
smoothed_rtt: initial_rtt,
max_rtt: initial_rtt,
rttvar: initial_rtt / 2,
has_first_rtt_sample: false,
max_ack_delay,
}
}
pub(crate) fn update_rtt(
&mut self, latest_rtt: Duration, mut ack_delay: Duration, now: Instant,
handshake_confirmed: bool,
) {
self.latest_rtt = latest_rtt;
if !self.has_first_rtt_sample {
self.min_rtt.reset(now, latest_rtt);
self.smoothed_rtt = latest_rtt;
self.max_rtt = latest_rtt;
self.rttvar = latest_rtt / 2;
self.has_first_rtt_sample = true;
return;
}
self.min_rtt.running_min(RTT_WINDOW, now, latest_rtt);
self.max_rtt = self.max_rtt.max(latest_rtt);
if handshake_confirmed {
ack_delay = ack_delay.min(self.max_ack_delay);
}
let mut adjusted_rtt = latest_rtt;
if latest_rtt >= *self.min_rtt + ack_delay {
adjusted_rtt = latest_rtt - ack_delay;
}
self.rttvar = self.rttvar * 3 / 4 +
Duration::from_nanos(
self.smoothed_rtt
.as_nanos()
.abs_diff(adjusted_rtt.as_nanos()) as u64 /
4,
);
self.smoothed_rtt = self.smoothed_rtt * 7 / 8 + adjusted_rtt / 8;
}
pub(crate) fn rtt(&self) -> Duration {
self.smoothed_rtt
}
#[allow(dead_code)]
pub(crate) fn latest_rtt(&self) -> Duration {
self.latest_rtt
}
pub(crate) fn rttvar(&self) -> Duration {
self.rttvar
}
pub(crate) fn min_rtt(&self) -> Option<Duration> {
if self.has_first_rtt_sample {
Some(*self.min_rtt)
} else {
None
}
}
pub(crate) fn max_rtt(&self) -> Option<Duration> {
if self.has_first_rtt_sample {
Some(self.max_rtt)
} else {
None
}
}
pub(crate) fn loss_delay(&self, time_thresh: f64) -> Duration {
self.latest_rtt
.max(self.smoothed_rtt)
.mul_f64(time_thresh)
.max(GRANULARITY)
}
}