Skip to content

Commit c06b5d5

Browse files
maxholmanclaude
andcommitted
fix(transport): use microsecond timestamps for sub-millisecond latency precision
as_millis() truncated sub-ms RTTs to 0.0, causing the display layer to show '—' (no measurement) for low-latency links. Switch Ping/Pong timestamps to microseconds so RTTs down to 1µs are preserved; latency_ms is now computed as (now_us - pong.timestamp_us) / 1000.0. Field number unchanged — wire-compatible. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent afc7671 commit c06b5d5

3 files changed

Lines changed: 26 additions & 26 deletions

File tree

crates/core/src/transport/protocol.rs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -203,14 +203,14 @@ impl ControlChannels {
203203

204204
// Periodic ping
205205
_ = ping_timer.tick() => {
206-
#[allow(clippy::cast_possible_truncation)] // millis since epoch fits u64 until ~year 584M
206+
#[allow(clippy::cast_possible_truncation)] // micros since epoch fits u64 until ~year 584K
207207
let ts = std::time::SystemTime::now()
208208
.duration_since(std::time::UNIX_EPOCH)
209209
.unwrap_or_default()
210-
.as_millis() as u64;
210+
.as_micros() as u64;
211211
let ping = ControlMessage {
212212
message: Some(control_message::Message::Ping(
213-
wallhack_wire::data::Ping { timestamp_ms: ts },
213+
wallhack_wire::data::Ping { timestamp_us: ts },
214214
)),
215215
};
216216
if let Err(e) = write_length_delimited_buf(stream, &ping, &mut write_buf).await {
@@ -249,7 +249,7 @@ impl ControlChannels {
249249
tracing::trace!("Control: received Ping, auto-replying Pong");
250250
let reply = ControlMessage {
251251
message: Some(control_message::Message::Pong(wallhack_wire::data::Pong {
252-
timestamp_ms: ping_msg.timestamp_ms,
252+
timestamp_us: ping_msg.timestamp_us,
253253
})),
254254
};
255255
if let Err(e) = write_length_delimited_buf(stream, &reply, write_buf).await {
@@ -259,13 +259,13 @@ impl ControlChannels {
259259
}
260260
Some(control_message::Message::Pong(pong)) => {
261261
#[allow(clippy::cast_possible_truncation)]
262-
let now_ms = std::time::SystemTime::now()
262+
let now_us = std::time::SystemTime::now()
263263
.duration_since(std::time::UNIX_EPOCH)
264264
.unwrap_or_default()
265-
.as_millis() as u64;
265+
.as_micros() as u64;
266266
#[allow(clippy::cast_precision_loss)]
267-
// ms-resolution latency; f64 mantissa exceeds plausible RTT range
268-
let latency_ms = now_ms.saturating_sub(pong.timestamp_ms) as f64;
267+
// us-resolution latency; f64 mantissa exceeds plausible RTT range
268+
let latency_ms = now_us.saturating_sub(pong.timestamp_us) as f64 / 1000.0;
269269
tracing::trace!(latency_ms, "Control: received Pong");
270270
if let Some(ref registry) = self.peer_registry
271271
&& let Some(ref name) = self.peer_name
@@ -762,7 +762,7 @@ mod tests {
762762
// Send a Ping instead of a Handshake as the first message.
763763
let bad_msg = ControlMessage {
764764
message: Some(control_message::Message::Ping(wallhack_wire::data::Ping {
765-
timestamp_ms: 0,
765+
timestamp_us: 0,
766766
})),
767767
};
768768
let mut buf = Vec::new();
@@ -829,11 +829,11 @@ mod tests {
829829
let ping_ts = std::time::SystemTime::now()
830830
.duration_since(std::time::UNIX_EPOCH)
831831
.unwrap()
832-
.as_millis() as u64;
832+
.as_micros() as u64;
833833

834834
let outgoing_ping = ControlMessage {
835835
message: Some(control_message::Message::Ping(wallhack_wire::data::Ping {
836-
timestamp_ms: ping_ts,
836+
timestamp_us: ping_ts,
837837
})),
838838
};
839839
let mut buf = Vec::new();
@@ -848,23 +848,23 @@ mod tests {
848848
.unwrap();
849849
match pong.message {
850850
Some(control_message::Message::Pong(p)) => {
851-
assert_eq!(p.timestamp_ms, ping_ts);
851+
assert_eq!(p.timestamp_us, ping_ts);
852852
}
853853
other => panic!("expected Pong, got: {other:?}"),
854854
}
855855

856-
// Now send a Pong with a timestamp 100ms in the past to test latency
857-
// computation. The control loop uses SystemTime, so we subtract from now.
856+
// Now send a Pong with a timestamp 100ms (100_000us) in the past to test
857+
// latency computation. The control loop uses SystemTime, so we subtract from now.
858858
#[allow(clippy::cast_possible_truncation)]
859859
let past_ts = std::time::SystemTime::now()
860860
.duration_since(std::time::UNIX_EPOCH)
861861
.unwrap()
862-
.as_millis() as u64
863-
- 100;
862+
.as_micros() as u64
863+
- 100_000;
864864

865865
let incoming_pong = ControlMessage {
866866
message: Some(control_message::Message::Pong(wallhack_wire::data::Pong {
867-
timestamp_ms: past_ts,
867+
timestamp_us: past_ts,
868868
})),
869869
};
870870
write_length_delimited_buf(&mut stream_a, &incoming_pong, &mut buf)
@@ -922,7 +922,7 @@ mod tests {
922922

923923
match msg.message {
924924
Some(control_message::Message::Ping(p)) => {
925-
assert!(p.timestamp_ms > 0, "ping timestamp should be non-zero");
925+
assert!(p.timestamp_us > 0, "ping timestamp should be non-zero");
926926
}
927927
other => panic!("expected Ping, got: {other:?}"),
928928
}
@@ -985,7 +985,7 @@ mod tests {
985985
// Send Ping, expect Pong back.
986986
let outgoing = ControlMessage {
987987
message: Some(control_message::Message::Ping(wallhack_wire::data::Ping {
988-
timestamp_ms: 42,
988+
timestamp_us: 42,
989989
})),
990990
};
991991
let mut buf = Vec::new();
@@ -1003,7 +1003,7 @@ mod tests {
10031003

10041004
match reply.message {
10051005
Some(control_message::Message::Pong(p)) => {
1006-
assert_eq!(p.timestamp_ms, 42);
1006+
assert_eq!(p.timestamp_us, 42);
10071007
}
10081008
other => panic!("expected Pong, got: {other:?}"),
10091009
}
@@ -1118,7 +1118,7 @@ mod tests {
11181118
for seq in 0..5_u64 {
11191119
let outgoing = ControlMessage {
11201120
message: Some(control_message::Message::Ping(wallhack_wire::data::Ping {
1121-
timestamp_ms: seq,
1121+
timestamp_us: seq,
11221122
})),
11231123
};
11241124
write_length_delimited_buf(&mut client_stream, &outgoing, &mut buf)
@@ -1135,7 +1135,7 @@ mod tests {
11351135

11361136
match reply.message {
11371137
Some(control_message::Message::Pong(p)) => {
1138-
assert_eq!(p.timestamp_ms, seq);
1138+
assert_eq!(p.timestamp_us, seq);
11391139
}
11401140
other => panic!("expected Pong #{seq}, got: {other:?}"),
11411141
}

crates/daemon/src/mode/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,11 @@ pub(crate) async fn send_ping(
8989
let ts = std::time::SystemTime::now()
9090
.duration_since(std::time::UNIX_EPOCH)
9191
.unwrap_or_default()
92-
.as_millis() as u64;
92+
.as_micros() as u64;
9393

9494
let ping_msg = ControlMessage {
9595
message: Some(control_message::Message::Ping(wallhack_wire::data::Ping {
96-
timestamp_ms: ts,
96+
timestamp_us: ts,
9797
})),
9898
};
9999

crates/wire/proto/data.proto

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,10 @@ message RoleHint {
260260

261261
// Ping request for latency measurement.
262262
message Ping {
263-
uint64 timestamp_ms = 1; // Optional timestamp for sender's reference
263+
uint64 timestamp_us = 1; // Optional timestamp for sender's reference (microseconds since UNIX epoch)
264264
}
265265

266266
// Pong response to ping.
267267
message Pong {
268-
uint64 timestamp_ms = 1; // Echo back the ping timestamp
268+
uint64 timestamp_us = 1; // Echo back the ping timestamp (microseconds since UNIX epoch)
269269
}

0 commit comments

Comments
 (0)