Skip to content

Commit

Permalink
fix: Add confirmed parameter to PTO calculation
Browse files Browse the repository at this point in the history
Rather than having the caller determine for which space a PTO should be calculated for.

Broken out of mozilla#1998

I'm ambivalent if we want this change - thoughts?
  • Loading branch information
larseggert committed Sep 18, 2024
1 parent d6279bf commit 3d57037
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 28 deletions.
14 changes: 9 additions & 5 deletions neqo-transport/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -629,13 +629,17 @@ impl Connection {
.unwrap()
}

fn confirmed(&self) -> bool {
self.state == State::Confirmed
}

/// Get the simplest PTO calculation for all those cases where we need
/// a value of this approximate order. Don't use this for loss recovery,
/// only use it where a more precise value is not important.
fn pto(&self) -> Duration {
self.paths.primary().map_or_else(
|| RttEstimate::default().pto(PacketNumberSpace::ApplicationData),
|p| p.borrow().rtt().pto(PacketNumberSpace::ApplicationData),
|| RttEstimate::default().pto(self.confirmed()),
|p| p.borrow().rtt().pto(self.confirmed()),
)
}

Expand Down Expand Up @@ -1058,7 +1062,7 @@ impl Connection {
if let Some(p) = self.paths.primary() {
let path = p.borrow();
let rtt = path.rtt();
let pto = rtt.pto(PacketNumberSpace::ApplicationData);
let pto = rtt.pto(self.confirmed());

let idle_time = self.idle_timeout.expiry(now, pto);
qtrace!([self], "Idle/keepalive timer {:?}", idle_time);
Expand Down Expand Up @@ -1525,7 +1529,7 @@ impl Connection {
let mut dcid = None;

qtrace!([self], "{} input {}", path.borrow(), hex(&**d));
let pto = path.borrow().rtt().pto(PacketNumberSpace::ApplicationData);
let pto = path.borrow().rtt().pto(self.confirmed());

// Handle each packet in the datagram.
while !slc.is_empty() {
Expand Down Expand Up @@ -2138,7 +2142,7 @@ impl Connection {
// or the PTO timer fired: probe.
true
} else {
let pto = path.borrow().rtt().pto(PacketNumberSpace::ApplicationData);
let pto = path.borrow().rtt().pto(self.confirmed());
if !builder.packet_empty() {
// The packet only contains an ACK. Check whether we want to
// force an ACK with a PING so we can stop tracking packets.
Expand Down
5 changes: 2 additions & 3 deletions neqo-transport/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ use crate::{
rtt::{RttEstimate, RttSource},
sender::PacketSender,
stats::FrameStats,
tracking::PacketNumberSpace,
Stats,
};

Expand Down Expand Up @@ -1020,7 +1019,7 @@ impl Path {
pub fn on_packets_lost(
&mut self,
prev_largest_acked_sent: Option<Instant>,
space: PacketNumberSpace,
confirmed: bool,
lost_packets: &[SentPacket],
stats: &mut Stats,
now: Instant,
Expand All @@ -1030,7 +1029,7 @@ impl Path {
let cwnd_reduced = self.sender.on_packets_lost(
self.rtt.first_sample_time(),
prev_largest_acked_sent,
self.rtt.pto(space), // Important: the base PTO, not adjusted.
self.rtt.pto(confirmed), // Important: the base PTO, not adjusted.
lost_packets,
stats,
now,
Expand Down
42 changes: 25 additions & 17 deletions neqo-transport/src/recovery/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,10 @@ impl LossRecovery {
}
}

const fn confirmed(&self) -> bool {
self.confirmed_time.is_some()
}

/// Returns (acked packets, lost packets)
#[allow(clippy::too_many_arguments)]
pub fn on_ack_received<R>(
Expand Down Expand Up @@ -627,7 +631,7 @@ impl LossRecovery {
// as we rely on the count of in-flight packets to determine whether to send
// another probe. Removing them too soon would result in not sending on PTO.
let loss_delay = primary_path.borrow().rtt().loss_delay();
let cleanup_delay = self.pto_period(primary_path.borrow().rtt(), pn_space);
let cleanup_delay = self.pto_period(primary_path.borrow().rtt());
let mut lost = Vec::new();
self.spaces.get_mut(pn_space).unwrap().detect_lost_packets(
now,
Expand All @@ -642,7 +646,7 @@ impl LossRecovery {
// backoff, so that we can determine persistent congestion.
primary_path.borrow_mut().on_packets_lost(
prev_largest_acked,
pn_space,
self.confirmed(),
&lost,
&mut self.stats.borrow_mut(),
now,
Expand Down Expand Up @@ -679,7 +683,7 @@ impl LossRecovery {
dropped
}

fn confirmed(&mut self, rtt: &RttEstimate, now: Instant) {
fn confirm(&mut self, rtt: &RttEstimate, now: Instant) {
debug_assert!(self.confirmed_time.is_none());
self.confirmed_time = Some(now);
// Up until now, the ApplicationData space has been ignored for PTO.
Expand Down Expand Up @@ -716,7 +720,7 @@ impl LossRecovery {
self.pto_state = None;

if space == PacketNumberSpace::Handshake {
self.confirmed(path.rtt(), now);
self.confirm(path.rtt(), now);
}
}

Expand Down Expand Up @@ -757,34 +761,37 @@ impl LossRecovery {
fn pto_period_inner(
rtt: &RttEstimate,
pto_state: Option<&PtoState>,
pn_space: PacketNumberSpace,
confirmed: bool,
fast_pto: u8,
) -> Duration {
// This is a complicated (but safe) way of calculating:
// base_pto * F * 2^pto_count
// where F = fast_pto / FAST_PTO_SCALE (== 1 by default)
let pto_count = pto_state.map_or(0, |p| u32::try_from(p.count).unwrap_or(0));
rtt.pto(pn_space)
rtt.pto(confirmed)
.checked_mul(u32::from(fast_pto) << min(pto_count, u32::BITS - u8::BITS))
.map_or(Duration::from_secs(3600), |p| p / u32::from(FAST_PTO_SCALE))
}

/// Get the current PTO period for the given packet number space.
/// Unlike calling `RttEstimate::pto` directly, this includes exponential backoff.
fn pto_period(&self, rtt: &RttEstimate, pn_space: PacketNumberSpace) -> Duration {
Self::pto_period_inner(rtt, self.pto_state.as_ref(), pn_space, self.fast_pto)
fn pto_period(&self, rtt: &RttEstimate) -> Duration {
Self::pto_period_inner(
rtt,
self.pto_state.as_ref(),
self.confirmed(),
self.fast_pto,
)
}

// Calculate PTO time for the given space.
fn pto_time(&self, rtt: &RttEstimate, pn_space: PacketNumberSpace) -> Option<Instant> {
if self.confirmed_time.is_none() && pn_space == PacketNumberSpace::ApplicationData {
None
} else {
self.spaces.get(pn_space).and_then(|space| {
space
.pto_base_time()
.map(|t| t + self.pto_period(rtt, pn_space))
})
self.spaces
.get(pn_space)
.and_then(|space| space.pto_base_time().map(|t| t + self.pto_period(rtt)))
}
}

Expand Down Expand Up @@ -859,21 +866,22 @@ impl LossRecovery {
qtrace!([self], "timeout {:?}", now);

let loss_delay = primary_path.borrow().rtt().loss_delay();
let confirmed = self.confirmed();

let mut lost_packets = Vec::new();
for space in self.spaces.iter_mut() {
let first = lost_packets.len(); // The first packet lost in this space.
let pto = Self::pto_period_inner(
primary_path.borrow().rtt(),
self.pto_state.as_ref(),
space.space,
confirmed,
self.fast_pto,
);
space.detect_lost_packets(now, loss_delay, pto, &mut lost_packets);

primary_path.borrow_mut().on_packets_lost(
space.largest_acked_sent_time,
space.space,
confirmed,
&lost_packets[first..],
&mut self.stats.borrow_mut(),
now,
Expand Down Expand Up @@ -1516,7 +1524,7 @@ mod tests {

// Expiring state after the PTO on the ApplicationData space has
// expired should result in setting a PTO state.
let default_pto = RttEstimate::default().pto(PacketNumberSpace::ApplicationData);
let default_pto = RttEstimate::default().pto(true);
let expected_pto = pn_time(2) + default_pto;
lr.discard(PacketNumberSpace::Handshake, expected_pto);
let profile = lr.send_profile(now());
Expand Down Expand Up @@ -1548,7 +1556,7 @@ mod tests {
ON_SENT_SIZE,
));

let handshake_pto = RttEstimate::default().pto(PacketNumberSpace::Handshake);
let handshake_pto = RttEstimate::default().pto(false);
let expected_pto = now() + handshake_pto;
assert_eq!(lr.pto_time(PacketNumberSpace::Initial), Some(expected_pto));
let profile = lr.send_profile(now());
Expand Down
5 changes: 2 additions & 3 deletions neqo-transport/src/rtt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use crate::{
qlog::{self, QlogMetric},
recovery::RecoveryToken,
stats::FrameStats,
tracking::PacketNumberSpace,
};

/// The smallest time that the system timer (via `sleep()`, `nanosleep()`,
Expand Down Expand Up @@ -163,9 +162,9 @@ impl RttEstimate {
self.smoothed_rtt
}

pub fn pto(&self, pn_space: PacketNumberSpace) -> Duration {
pub fn pto(&self, confirmed: bool) -> Duration {
let mut t = self.estimate() + max(4 * self.rttvar, GRANULARITY);
if pn_space == PacketNumberSpace::ApplicationData {
if confirmed {
t += self.ack_delay.max();
}
t
Expand Down

0 comments on commit 3d57037

Please sign in to comment.