From 4a5a51e6e550456c75d1a2182b0eafb9c3033ec0 Mon Sep 17 00:00:00 2001 From: Lorenz Leutgeb Date: Thu, 4 Dec 2025 20:30:20 +0100 Subject: [PATCH] node: Do not mix monotonic and system time The `struct Epoch` introduced in e404f1038f461264f9395742ef74f5b710be inadvertently made `radicle-node` suffer from https://github.com/rust-lang/rust/issues/87906 That is, updating of the "current time" was skewed *way more* than expected by a slowly ticking monotonic clock. Such slow ticking can be caused by suspending the system. --- crates/radicle-node/src/reactor.rs | 4 +-- crates/radicle-node/src/wire.rs | 40 ++---------------------------- 2 files changed, 4 insertions(+), 40 deletions(-) diff --git a/crates/radicle-node/src/reactor.rs b/crates/radicle-node/src/reactor.rs index c255dfd7..467eded3 100644 --- a/crates/radicle-node/src/reactor.rs +++ b/crates/radicle-node/src/reactor.rs @@ -211,7 +211,7 @@ pub trait ReactionHandler: Send + Iterator Runtime { // Blocking let res = self.poll.poll(&mut events, Some(timeout)); + self.service.tick(); let tick = Instant::now(); - self.service.tick(tick); // The way this is currently used basically ignores which keys have // timed out. So as long as *something* timed out, we wake the service. diff --git a/crates/radicle-node/src/wire.rs b/crates/radicle-node/src/wire.rs index 40b9125a..6736bc33 100644 --- a/crates/radicle-node/src/wire.rs +++ b/crates/radicle-node/src/wire.rs @@ -291,35 +291,6 @@ impl Peers { } } -/// The epoch time of when the node started. -struct Epoch { - /// The system time when the node started. - started_time: SystemTime, - /// The instant when the node started. - started_at: Instant, -} - -impl Epoch { - /// Construct a new [`Epoch`]. - fn new(started_time: SystemTime, started_at: Instant) -> Self { - Self { - started_time, - started_at, - } - } - - /// Construct an [`Epoch`] where both values are recorded using their - /// equivalent `now` constructors. - fn now() -> Self { - Self::new(SystemTime::now(), Instant::now()) - } - - /// Get the elapsed [`SystemTime`] given a later [`Instant`]. - fn elapsed_time(&self, later: Instant) -> SystemTime { - self.started_time + (later - self.started_at) - } -} - /// Wire protocol implementation for a set of peers. pub(crate) struct Wire + Ecdh> { /// Backing service instance. @@ -342,8 +313,6 @@ pub(crate) struct Wire + E peers: Peers, /// A (practically) infinite source of tokens to identify transports and listeners. tokens: Tokens, - /// Record of system time and instant when the node started. - epoch: Epoch, } impl Wire @@ -366,14 +335,9 @@ where listening: RandomMap::default(), peers: Peers(RandomMap::default()), tokens: Tokens::default(), - epoch: Epoch::now(), } } - fn time(&self, instant: Instant) -> SystemTime { - self.epoch.elapsed_time(instant) - } - pub fn listen(&mut self, socket: Listener) { let token = self.tokens.advance(); self.listening.insert(token, socket.local_addr()); @@ -532,7 +496,7 @@ where type Listener = Listener; type Transport = Transport>; - fn tick(&mut self, time: Instant) { + fn tick(&mut self) { self.metrics.open_channels = self .peers .iter() @@ -546,7 +510,7 @@ where .sum(); self.metrics.worker_queue_size = self.worker.len(); - self.service.tick(self.time(time).into(), &self.metrics); + self.service.tick(SystemTime::now().into(), &self.metrics); } fn timer_reacted(&mut self) {