refactor(client): separate accepted negotiation state from peer evidence

This commit is contained in:
Nils Andreas Svee committed 2026-10-04 18:19:28 +07:00
1 parent f62e6a9953
commit d467211f41
32 files changed
+507 -447

No files matched your search

+23 -26
View File
@@ -1,6 +1,6 @@
use std::time::{Duration, Instant};
use crate::{error::ClientError, session::NegotiatedParams};
use crate::{error::ClientError, session::AcceptedSessionParameters};
#[derive(Debug)]
pub(crate) struct ProbeSchedule {
@@ -18,24 +18,19 @@ pub(crate) struct ScheduleCommit {
impl ProbeSchedule {
pub(crate) fn new(
start_at: Instant,
negotiated: &NegotiatedParams,
accepted: &AcceptedSessionParameters,
) -> Result<Self, ClientError> {
let interval_ns = u64::try_from(negotiated.params.interval_ns)
.expect("validated positive negotiated interval");
let interval = Duration::from_nanos(interval_ns);
let end_at = if negotiated.params.duration_ns > 0 {
let duration_ns = u64::try_from(negotiated.params.duration_ns)
.expect("validated positive negotiated duration");
Some(
let interval = accepted.interval;
let end_at = accepted
.duration
.map(|duration| {
start_at
.checked_add(Duration::from_nanos(duration_ns))
.checked_add(duration)
.ok_or_else(|| ClientError::NegotiationRejected {
reason: "duration is too large to schedule".to_owned(),
})?,
)
} else {
None
};
})
})
.transpose()?;
Ok(Self {
end_at,
@@ -166,15 +161,17 @@ mod tests {
fn lifetime_deadline_exists_only_for_an_unfinished_finite_schedule() {
let start = Instant::now();
let end = start + Duration::from_millis(500);
let mut negotiated = NegotiatedParams {
params: irtt_proto::Params {
duration_ns: 500_000_000,
interval_ns: 1_000_000,
..irtt_proto::Params::default()
},
restrictions: Vec::new(),
let mut accepted = AcceptedSessionParameters {
duration: Some(Duration::from_millis(500)),
interval: Duration::from_millis(1),
length: 0,
received_stats: irtt_proto::ReceivedStats::None,
stamp_at: irtt_proto::StampAt::None,
clock: irtt_proto::Clock::Both,
dscp: 0,
server_fill: None,
};
let mut finite = ProbeSchedule::new(start, &negotiated).unwrap();
let mut finite = ProbeSchedule::new(start, &accepted).unwrap();
assert_eq!(finite.end_deadline(), Some(end));
assert!(finite.permit_probe_at(end - Duration::from_nanos(1)));
assert_eq!(finite.end_deadline(), Some(end));
@@ -182,7 +179,7 @@ mod tests {
assert!(finite.is_finished());
assert_eq!(finite.end_deadline(), None);
let mut finite = ProbeSchedule::new(start, &negotiated).unwrap();
let mut finite = ProbeSchedule::new(start, &accepted).unwrap();
let commit = finite
.preflight_managed_commit(start, end - Duration::from_nanos(1))
.unwrap();
@@ -190,8 +187,8 @@ mod tests {
assert!(finite.is_finished());
assert_eq!(finite.end_deadline(), None);
negotiated.params.duration_ns = 0;
let mut continuous = ProbeSchedule::new(start, &negotiated).unwrap();
accepted.duration = None;
let mut continuous = ProbeSchedule::new(start, &accepted).unwrap();
assert!(continuous.permit_probe_at(end));
assert_eq!(continuous.end_deadline(), None);
}
+25 -23
View File
@@ -424,8 +424,8 @@ impl TargetCounters {
}
ClientEvent::EchoSent { .. }
| ClientEvent::EchoLoss { .. }
| ClientEvent::SessionStarted { .. }
| ClientEvent::NoTestCompleted { .. }
| ClientEvent::SessionStarted(_)
| ClientEvent::NoTestCompleted(_)
| ClientEvent::SessionClosed { .. } => {}
}
}
@@ -1138,13 +1138,8 @@ impl ManagedClientTask {
self.targets[index].state = TargetState::Opening { client, open };
false
}
Poll::Ready(Ok(OpenOutcome::Started {
event, negotiated, ..
})) => {
let ClientEvent::SessionStarted { at: opened_at, .. } = &event else {
unreachable!("Started carries SessionStarted");
};
match ProbeSchedule::new(opened_at.mono, &negotiated) {
Poll::Ready(Ok(OpenOutcome::Started(started))) => {
match ProbeSchedule::new(started.at.mono, &started.negotiation.accepted) {
Ok(schedule) => self.targets[index].schedule = Some(schedule),
Err(error) => {
return self.begin_open_session_failure(
@@ -1157,7 +1152,10 @@ impl ManagedClientTask {
)
}
}
self.publish_client_events(index, vec![event]);
self.publish_client_events(
index,
vec![ClientEvent::SessionStarted(started)],
);
if self.state == DriverState::Stopping
|| self.effective_retirement(index).is_some()
{
@@ -1174,8 +1172,11 @@ impl ManagedClientTask {
true
}
}
Poll::Ready(Ok(OpenOutcome::NoTestCompleted { event, .. })) => {
self.publish_client_events(index, vec![event]);
Poll::Ready(Ok(OpenOutcome::NoTestCompleted(completed))) => {
self.publish_client_events(
index,
vec![ClientEvent::NoTestCompleted(completed)],
);
let end_reason = if self.state == DriverState::Stopping
|| self.effective_retirement(index).is_some()
{
@@ -2408,13 +2409,15 @@ mod tests {
runtime.block_on(async {
let start = Instant::now();
let end = start + Duration::from_millis(500);
let negotiated = crate::NegotiatedParams {
params: irtt_proto::Params {
duration_ns: 500_000_000,
interval_ns: 1_000_000,
..irtt_proto::Params::default()
},
restrictions: Vec::new(),
let accepted = crate::AcceptedSessionParameters {
duration: Some(Duration::from_millis(500)),
interval: Duration::from_millis(1),
length: 0,
received_stats: irtt_proto::ReceivedStats::None,
stamp_at: irtt_proto::StampAt::None,
clock: irtt_proto::Clock::Both,
dscp: 0,
server_fill: None,
};
for pacing in [ManagedPacing::Burst, ManagedPacing::Staggered] {
let (mut task, _handle) = ManagedClient::task(
@@ -2434,8 +2437,7 @@ mod tests {
for send_waiting in [false, true] {
task.targets[0].send_waiting = send_waiting;
task.targets[0].schedule =
Some(ProbeSchedule::new(start, &negotiated).unwrap());
task.targets[0].schedule = Some(ProbeSchedule::new(start, &accepted).unwrap());
let expected = if pacing == ManagedPacing::Burst && !send_waiting {
start
} else {
@@ -2451,8 +2453,8 @@ mod tests {
assert_eq!(task.next_deadline(), None);
}
let mut continuous = negotiated.clone();
continuous.params.duration_ns = 0;
let mut continuous = accepted.clone();
continuous.duration = None;
task.targets[0].schedule = Some(ProbeSchedule::new(start, &continuous).unwrap());
assert_eq!(task.next_deadline(), None);
}