From f41f63b92271a53989070ab4ecc34a934d9cdc67 Mon Sep 17 00:00:00 2001 From: Nils Andreas Svee Date: Fri, 1 May 2026 10:05:29 +0700 Subject: [PATCH] feat(client): implement SignedDuration for precise RTT tracking --- crates/irtt-cli/src/lib.rs | 4 +++- crates/irtt-client/src/client.rs | 22 ++++++++++++++++++++-- crates/irtt-client/src/event.rs | 7 +++++++ crates/irtt-client/src/lib.rs | 2 +- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/crates/irtt-cli/src/lib.rs b/crates/irtt-cli/src/lib.rs index d43219b..7864a08 100644 --- a/crates/irtt-cli/src/lib.rs +++ b/crates/irtt-cli/src/lib.rs @@ -486,7 +486,7 @@ fn escape_value(value: &str) -> String { mod tests { use super::*; use clap::Parser; - use irtt_client::{ClientTimestamp, PacketMeta, RttSample}; + use irtt_client::{ClientTimestamp, PacketMeta, RttSample, SignedDuration}; use irtt_proto::{Params, PROTOCOL_VERSION}; use std::{ net::{IpAddr, Ipv4Addr, SocketAddr}, @@ -521,6 +521,8 @@ mod tests { raw: Duration::from_micros(1500), adjusted: Some(Duration::from_micros(1200)), effective: Duration::from_micros(1200), + adjusted_signed: Some(SignedDuration { ns: 1_200_000 }), + effective_signed: SignedDuration { ns: 1_200_000 }, }, server_timing: Some(ServerTiming { receive_wall_ns: Some(1_000), diff --git a/crates/irtt-client/src/client.rs b/crates/irtt-client/src/client.rs index 6ace591..c0d7487 100644 --- a/crates/irtt-client/src/client.rs +++ b/crates/irtt-client/src/client.rs @@ -592,14 +592,26 @@ fn compute_rtt( let adjusted = server_processing.and_then(|sp| raw.checked_sub(sp)); let effective = adjusted.unwrap_or(raw); + let adjusted_signed = server_processing.map(|sp| SignedDuration { + ns: duration_ns_i128(raw) - duration_ns_i128(sp), + }); + let effective_signed = adjusted_signed.unwrap_or(SignedDuration { + ns: duration_ns_i128(raw), + }); RttSample { raw, adjusted, effective, + adjusted_signed, + effective_signed, } } +fn duration_ns_i128(duration: Duration) -> i128 { + i128::try_from(duration.as_nanos()).unwrap_or(i128::MAX) +} + fn compute_server_processing(ts: &TimestampFields) -> Option { if let (Some(recv_mono), Some(send_mono)) = (ts.recv_mono, ts.send_mono) { let diff = send_mono.checked_sub(recv_mono)?; @@ -1726,13 +1738,14 @@ mod tests { #[test] fn server_processing_greater_than_raw_does_not_underflow() { + let base = Instant::now(); let rtt = compute_rtt( &ClientTimestamp { - mono: Instant::now(), + mono: base, wall: SystemTime::now(), }, &ClientTimestamp { - mono: Instant::now() + Duration::from_nanos(1), + mono: base + Duration::from_nanos(1), wall: SystemTime::now(), }, &TimestampFields { @@ -1743,6 +1756,11 @@ mod tests { ); assert!(rtt.adjusted.is_none()); assert_eq!(rtt.effective, rtt.raw); + assert_eq!( + rtt.adjusted_signed, + Some(SignedDuration { ns: -999_999_999 }) + ); + assert_eq!(rtt.effective_signed, SignedDuration { ns: -999_999_999 }); } #[test] diff --git a/crates/irtt-client/src/event.rs b/crates/irtt-client/src/event.rs index a4a370b..9b20b12 100644 --- a/crates/irtt-client/src/event.rs +++ b/crates/irtt-client/src/event.rs @@ -97,6 +97,13 @@ pub struct RttSample { pub raw: Duration, pub adjusted: Option, pub effective: Duration, + pub adjusted_signed: Option, + pub effective_signed: SignedDuration, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SignedDuration { + pub ns: i128, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/irtt-client/src/lib.rs b/crates/irtt-client/src/lib.rs index ae8ab5d..444ef12 100644 --- a/crates/irtt-client/src/lib.rs +++ b/crates/irtt-client/src/lib.rs @@ -15,7 +15,7 @@ pub use config::{ClientConfig, NegotiationPolicy, RecvBudget, RunMode, SocketCon pub use error::{ClientError, EventSubscriptionError}; pub use event::{ ClientEvent, OneWayDelaySample, OpenOutcome, PacketMeta, ReceivedStatsSample, RttSample, - ServerTiming, WarningKind, + ServerTiming, SignedDuration, WarningKind, }; pub use managed::{ CancellationToken, EventHub, EventSubscription, ManagedClient, ManagedClientSession,