Merge branch 'fix/managed-finite-lifetime-wake'
Rust CI / cargo fmt (push) Successful in 19s
Rust CI / cargo check (x86_64-unknown-linux-gnu) (push) Successful in 35s
Rust CI / cargo clippy (push) Successful in 37s
Rust CI / cargo check (x86_64-unknown-linux-musl) (push) Failing after 7s
Rust CI / cargo test (x86_64-unknown-linux-musl) (push) Failing after 8s
Rust CI / cargo doc (push) Successful in 23s
Rust CI / cargo test (x86_64-unknown-linux-gnu) (push) Successful in 1m16s
Fuzz Smoke / cargo fuzz smoke (push) Failing after 27s
Supply chain / cargo-deny advisories (push) Successful in 9s
Supply chain / actionlint (push) Successful in 3s
Supply chain / cargo-deny bans licenses sources (push) Successful in 8s
Netem Interop / netem interop (push) Successful in 1m3s
Rust CI / cargo fmt (push) Successful in 19s
Rust CI / cargo check (x86_64-unknown-linux-gnu) (push) Successful in 35s
Rust CI / cargo clippy (push) Successful in 37s
Rust CI / cargo check (x86_64-unknown-linux-musl) (push) Failing after 7s
Rust CI / cargo test (x86_64-unknown-linux-musl) (push) Failing after 8s
Rust CI / cargo doc (push) Successful in 23s
Rust CI / cargo test (x86_64-unknown-linux-gnu) (push) Successful in 1m16s
Fuzz Smoke / cargo fuzz smoke (push) Failing after 27s
Supply chain / cargo-deny advisories (push) Successful in 9s
Supply chain / actionlint (push) Successful in 3s
Supply chain / cargo-deny bans licenses sources (push) Successful in 8s
Netem Interop / netem interop (push) Successful in 1m3s
This commit is contained in:
2 files changed
+115
No files matched your search
@@ -48,6 +48,11 @@ impl ProbeSchedule {
|
|||||||
self.next_send_at
|
self.next_send_at
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wake for finite expiry independently of send readiness until finished.
|
||||||
|
pub(crate) fn end_deadline(&self) -> Option<Instant> {
|
||||||
|
self.end_at.filter(|_| !self.is_finished())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn interval(&self) -> Duration {
|
pub(crate) fn interval(&self) -> Duration {
|
||||||
self.interval
|
self.interval
|
||||||
}
|
}
|
||||||
@@ -152,3 +157,42 @@ pub(crate) fn instant_abs_diff(left: Instant, right: Instant) -> Duration {
|
|||||||
.or_else(|| right.checked_duration_since(left))
|
.or_else(|| right.checked_duration_since(left))
|
||||||
.unwrap_or(Duration::ZERO)
|
.unwrap_or(Duration::ZERO)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
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 finite = ProbeSchedule::new(start, &negotiated).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));
|
||||||
|
assert!(!finite.permit_probe_at(end));
|
||||||
|
assert!(finite.is_finished());
|
||||||
|
assert_eq!(finite.end_deadline(), None);
|
||||||
|
|
||||||
|
let mut finite = ProbeSchedule::new(start, &negotiated).unwrap();
|
||||||
|
let commit = finite
|
||||||
|
.preflight_managed_commit(start, end - Duration::from_nanos(1))
|
||||||
|
.unwrap();
|
||||||
|
finite.commit(commit);
|
||||||
|
assert!(finite.is_finished());
|
||||||
|
assert_eq!(finite.end_deadline(), None);
|
||||||
|
|
||||||
|
negotiated.params.duration_ns = 0;
|
||||||
|
let mut continuous = ProbeSchedule::new(start, &negotiated).unwrap();
|
||||||
|
assert!(continuous.permit_probe_at(end));
|
||||||
|
assert_eq!(continuous.end_deadline(), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1936,6 +1936,12 @@ impl ManagedClientTask {
|
|||||||
non_send_deadline = non_send_deadline
|
non_send_deadline = non_send_deadline
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(client.next_probe_timeout_deadline())
|
.chain(client.next_probe_timeout_deadline())
|
||||||
|
.chain(
|
||||||
|
target
|
||||||
|
.schedule
|
||||||
|
.as_ref()
|
||||||
|
.and_then(ProbeSchedule::end_deadline),
|
||||||
|
)
|
||||||
.min();
|
.min();
|
||||||
if target.desired && target.retirement.is_none() && !target.send_waiting {
|
if target.desired && target.retirement.is_none() && !target.send_waiting {
|
||||||
send_deadline = send_deadline
|
send_deadline = send_deadline
|
||||||
@@ -2399,6 +2405,71 @@ fn close_timeout_failure() -> ManagedTargetFailure {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
// Real EAGAIN needs privileged queue shaping. Inspect only the owning
|
||||||
|
// deadline selection here, using a production socket without sending.
|
||||||
|
#[test]
|
||||||
|
fn finite_lifetime_deadline_survives_send_waiting_and_stagger_gating() {
|
||||||
|
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
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(),
|
||||||
|
};
|
||||||
|
for pacing in [ManagedPacing::Burst, ManagedPacing::Staggered] {
|
||||||
|
let (mut task, _handle) = ManagedClient::task(
|
||||||
|
ManagedClientConfig {
|
||||||
|
pacing,
|
||||||
|
..ManagedClientConfig::default()
|
||||||
|
},
|
||||||
|
vec![ManagedTargetConfig::new("target", "127.0.0.1:9")],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let client = AsyncClient::connect(ClientConfig {
|
||||||
|
server_addr: "127.0.0.1:9".into(),
|
||||||
|
..ClientConfig::default()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
task.state = DriverState::Running;
|
||||||
|
task.install_target_state(0, TargetState::Active { client });
|
||||||
|
task.send_gate = Some(end + Duration::from_secs(1));
|
||||||
|
|
||||||
|
for send_waiting in [false, true] {
|
||||||
|
task.targets[0].send_waiting = send_waiting;
|
||||||
|
task.targets[0].schedule =
|
||||||
|
Some(ProbeSchedule::new(start, &negotiated).unwrap());
|
||||||
|
let expected = if pacing == ManagedPacing::Burst && !send_waiting {
|
||||||
|
start
|
||||||
|
} else {
|
||||||
|
end
|
||||||
|
};
|
||||||
|
assert_eq!(task.next_deadline(), Some(expected));
|
||||||
|
|
||||||
|
task.targets[0]
|
||||||
|
.schedule
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.permit_probe_at(end);
|
||||||
|
assert_eq!(task.next_deadline(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut continuous = negotiated.clone();
|
||||||
|
continuous.params.duration_ns = 0;
|
||||||
|
task.targets[0].schedule = Some(ProbeSchedule::new(start, &continuous).unwrap());
|
||||||
|
assert_eq!(task.next_deadline(), None);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn release_gate_only_delays_an_existing_send_deadline() {
|
fn release_gate_only_delays_an_existing_send_deadline() {
|
||||||
let earlier = Instant::now();
|
let earlier = Instant::now();
|
||||||
|
|||||||
Reference in new issue
Block a user