in neqo-transport/src/cc/classic_cc.rs [183:274]
fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], rtt_est: &RttEstimate, now: Instant) {
let mut is_app_limited = true;
let mut new_acked = 0;
for pkt in acked_pkts {
qtrace!(
"packet_acked this={self:p}, pn={}, ps={}, ignored={}, lost={}, rtt_est={rtt_est:?}",
pkt.pn(),
pkt.len(),
i32::from(!pkt.cc_outstanding()),
i32::from(pkt.lost()),
);
if !pkt.cc_outstanding() {
continue;
}
if pkt.pn() < self.first_app_limited {
is_app_limited = false;
}
// BIF is set to 0 on a path change, but in case that was because of a simple rebinding
// event, we may still get ACKs for packets sent before the rebinding.
self.bytes_in_flight = self.bytes_in_flight.saturating_sub(pkt.len());
if !self.after_recovery_start(pkt) {
// Do not increase congestion window for packets sent before
// recovery last started.
continue;
}
if self.state.in_recovery() {
self.set_state(State::CongestionAvoidance, now);
qlog::metrics_updated(&self.qlog, &[QlogMetric::InRecovery(false)], now);
}
new_acked += pkt.len();
}
if is_app_limited {
self.cc_algorithm.on_app_limited();
qdebug!("on_packets_acked this={self:p}, limited=1, bytes_in_flight={}, cwnd={}, state={:?}, new_acked={new_acked}", self.bytes_in_flight, self.congestion_window, self.state);
return;
}
// Slow start, up to the slow start threshold.
if self.congestion_window < self.ssthresh {
self.acked_bytes += new_acked;
let increase = min(self.ssthresh - self.congestion_window, self.acked_bytes);
self.congestion_window += increase;
self.acked_bytes -= increase;
qdebug!("[{self}] slow start += {increase}");
if self.congestion_window == self.ssthresh {
// This doesn't look like it is necessary, but it can happen
// after persistent congestion.
self.set_state(State::CongestionAvoidance, now);
}
}
// Congestion avoidance, above the slow start threshold.
if self.congestion_window >= self.ssthresh {
// The following function return the amount acked bytes a controller needs
// to collect to be allowed to increase its cwnd by MAX_DATAGRAM_SIZE.
let bytes_for_increase = self.cc_algorithm.bytes_for_cwnd_increase(
self.congestion_window,
new_acked,
rtt_est.minimum(),
self.max_datagram_size(),
now,
);
debug_assert!(bytes_for_increase > 0);
// If enough credit has been accumulated already, apply them gradually.
// If we have sudden increase in allowed rate we actually increase cwnd gently.
if self.acked_bytes >= bytes_for_increase {
self.acked_bytes = 0;
self.congestion_window += self.max_datagram_size();
}
self.acked_bytes += new_acked;
if self.acked_bytes >= bytes_for_increase {
self.acked_bytes -= bytes_for_increase;
self.congestion_window += self.max_datagram_size(); // or is this the current MTU?
}
// The number of bytes we require can go down over time with Cubic.
// That might result in an excessive rate of increase, so limit the number of unused
// acknowledged bytes after increasing the congestion window twice.
self.acked_bytes = min(bytes_for_increase, self.acked_bytes);
}
qlog::metrics_updated(
&self.qlog,
&[
QlogMetric::CongestionWindow(self.congestion_window),
QlogMetric::BytesInFlight(self.bytes_in_flight),
],
now,
);
qdebug!("[{self}] on_packets_acked this={self:p}, limited=0, bytes_in_flight={}, cwnd={}, state={:?}, new_acked={new_acked}", self.bytes_in_flight, self.congestion_window, self.state);
}