From db4febd65ae4ca1c7370f7aa7eedb5e63e83ce47 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 15 Oct 2026 12:12:29 +0000 Subject: [PATCH] Forget abandoned virtual-clock sleeps, drop TransportError::IpBlocked A dropped Clock::sleep left its waker in VIRTUAL_SLEEPERS until the virtual time passed its deadline, which a later test case's reactor may never do. Prune closed wakers when sleeping and when the time moves. Nothing constructs IpBlocked: a blocked address is a failed DNS lookup, as in Python. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01R5b7Ruy4cgt27Mjq6Ptxbb --- rust/src/clock.rs | 12 ++++++++++++ rust/src/federation/transport.rs | 9 +++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/rust/src/clock.rs b/rust/src/clock.rs index 3421dd1ae7..00e3d60ef1 100644 --- a/rust/src/clock.rs +++ b/rust/src/clock.rs @@ -83,8 +83,12 @@ pub fn set_virtual_time_msec(millis: u64) { let due = { let mut sleepers = VIRTUAL_SLEEPERS.lock().expect("poisoned"); + // Drop the sleeps nobody waits on any more. A later test case's + // reactor may start before their deadline, so they would otherwise + // linger. let (due, pending) = sleepers .drain(..) + .filter(|(_, waker)| !waker.is_closed()) .partition::, _>(|(wake_at, _)| *wake_at <= millis); *sleepers = pending; due @@ -165,6 +169,9 @@ impl Clock { if wake_at <= now { let _ = tx.send(()); } else { + // Also drop abandoned sleeps here, so they cannot + // pile up while the virtual time stands still. + sleepers.retain(|(_, waker)| !waker.is_closed()); sleepers.push((wake_at, tx)); } Some(rx) @@ -243,6 +250,11 @@ mod tests { set_virtual_time_msec(500); assert!(sleep.as_mut().poll(&mut cx).is_ready()); + // A dropped sleep is forgotten. + drop(clock.sleep(Duration::from_millis(1_000))); + set_virtual_time_msec(500); + assert!(VIRTUAL_SLEEPERS.lock().unwrap().is_empty()); + // A zero-length sleep is ready at once. let mut sleep = std::pin::pin!(clock.sleep(Duration::ZERO)); assert!(sleep.as_mut().poll(&mut cx).is_ready()); diff --git a/rust/src/federation/transport.rs b/rust/src/federation/transport.rs index a38c8d33a8..36928b4c79 100644 --- a/rust/src/federation/transport.rs +++ b/rust/src/federation/transport.rs @@ -53,18 +53,16 @@ pub trait FederationTransport: Send + Sync + 'static { /// Why a request got no response. /// /// The request layer needs to tell these apart: a DNS failure is not -/// retried when the caller asked for `retry_on_dns_fail=False`, and a -/// blocked IP address is reported as such. +/// retried when the caller asked for `retry_on_dns_fail=False`. #[derive(Debug)] pub enum TransportError { /// Looking up the server's addresses (SRV or A/AAAA) failed, or found - /// nothing we may connect to. + /// nothing we may connect to. An address the IP range blocklist covers + /// counts as not found, as in Python. Dns(anyhow::Error), /// The server publishes a single SRV record with target `.`, which means /// it offers no federation service. ServiceUnavailable(String), - /// The destination is an IP address that the IP range blocklist covers. - IpBlocked(std::net::IpAddr), /// Every TCP connection attempt failed, or the proxy refused the tunnel. Connect(anyhow::Error), /// The TLS handshake or certificate verification failed. @@ -92,7 +90,6 @@ impl fmt::Display for TransportError { match self { TransportError::Dns(e) => write!(f, "DNS lookup failed: {e:#}"), TransportError::ServiceUnavailable(name) => write!(f, "service {name} unavailable"), - TransportError::IpBlocked(ip) => write!(f, "IP address {ip} blocked"), TransportError::Connect(e) => write!(f, "connection failed: {e:#}"), TransportError::Tls(e) => write!(f, "TLS failed: {e:#}"), TransportError::InvalidRequest(e) => write!(f, "invalid request: {e:#}"),