From d74ac8da5c6ba9a2f81ff61599d16a013c3f8303 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 23 Oct 2026 15:01:56 +0200 Subject: [PATCH] Launcher: presence freshness treats a stamp after the reading as age zero; pin the bound literally Reading the clock after the cached result still left the answer to ordering; the one place freshness is decided is the age subtraction, and monotonic time cannot run backwards, so a result stamped after the reading is fresher than it and ages zero. The bound test names 86400 and 86401 so raising the constant is caught. Co-Authored-By: Claude Opus 5.5 --- src/launcher.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/launcher.rs b/src/launcher.rs index 13937f6..435f8b1 100644 --- a/src/launcher.rs +++ b/src/launcher.rs @@ -902,12 +902,17 @@ impl Shared { /// dead presence acceptor does not degrade presence: it fails the unit. fn serving_state(&self) -> &'static str { let stale = u64::try_from(HEALTH_STALE.as_millis()).unwrap_or(u64::MAX); - // The result is read before the clock: a stamp is taken before it is - // stored, so a later reading of the monotonic clock is never behind it, - // whatever the presence thread does between the two. - let probed = self.probed.lock().ok().and_then(|probed| *probed); let now = self.clock.monotonic_millis(); - let healthy = probed.is_some_and(|(healthy, at)| healthy && now.saturating_sub(at) <= stale); + // A probe can finish between this thread reading the clock and locking + // the result, so a stamp later than `now` is a result fresher than the + // reading, age zero: monotonic time does not run backwards, so the stamp + // is not wrong. + let healthy = self + .probed + .lock() + .ok() + .and_then(|probed| *probed) + .is_some_and(|(healthy, at)| healthy && now.saturating_sub(at) <= stale); if healthy { "active" } else { "degraded" } } @@ -2448,8 +2453,8 @@ mod tests { .to_vec(); for flag in ["--lease-wait-secs", "--startup-secs", "--heartbeat-secs"] { for (value, accepted) in [ - (MAX_DECLARED_SECONDS.to_string(), true), - ((MAX_DECLARED_SECONDS + 1).to_string(), false), + ("86400".to_owned(), true), + ("86401".to_owned(), false), (u64::MAX.to_string(), false), ] { let mut args = base.clone();