Skip to content

Commit 2f50b49

Browse files
olix0rseanmonstar
authored andcommitted
Avoid time operations that can panic
We have reports of runtime panics (linkerd/linkerd2#7748) that sound a lot like rust-lang/rust#86470. We don't have any evidence that these panics originate in tower, but we have some potentialy flawed `Instant` arithmetic that could panic in this way. Even though this is almost definitely a bug in Rust, it seems most prudent to actively avoid the uses of `Instant` that are prone to this bug. This change replaces uses of `Instant::elapsed` and `Instant::sub` with calls to `Instant::saturating_duration_since` to prevent this class of panic. These fixes should ultimately be made in the standard library, but this change lets us avoid this problem while we wait for those fixes. See also hyperium/hyper#2746
1 parent 20d7b55 commit 2f50b49

File tree

4 files changed

+6
-5
lines changed

4 files changed

+6
-5
lines changed

tower/src/hedge/latency.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ where
8282
let this = self.project();
8383

8484
let rsp = ready!(this.inner.poll(cx)).map_err(Into::into)?;
85-
let duration = Instant::now() - *this.start;
85+
let duration = Instant::now().saturating_duration_since(*this.start);
8686
this.rec.record(duration);
8787
Poll::Ready(Ok(rsp))
8888
}

tower/src/hedge/rotating_histogram.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl RotatingHistogram {
3939
}
4040

4141
fn maybe_rotate(&mut self) {
42-
let delta = Instant::now() - self.last_rotation;
42+
let delta = Instant::now().saturating_duration_since(self.last_rotation);
4343
// TODO: replace with delta.duration_div when it becomes stable.
4444
let rotations = (nanos(delta) / nanos(self.period)) as u32;
4545
if rotations >= 2 {

tower/src/load/peak_ewma.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ const NANOS_PER_MILLI: f64 = 1_000_000.0;
9191
impl<S, C> PeakEwma<S, C> {
9292
/// Wraps an `S`-typed service so that its load is tracked by the EWMA of its peak latency.
9393
pub fn new(service: S, default_rtt: Duration, decay_ns: f64, completion: C) -> Self {
94+
debug_assert!(decay_ns > 0.0, "decay_ns must be positive");
9495
Self {
9596
service,
9697
decay_ns,
@@ -241,7 +242,7 @@ impl RttEstimate {
241242
recv_at,
242243
sent_at
243244
);
244-
let rtt = nanos(recv_at - sent_at);
245+
let rtt = nanos(recv_at.saturating_duration_since(sent_at));
245246

246247
let now = Instant::now();
247248
debug_assert!(
@@ -264,7 +265,7 @@ impl RttEstimate {
264265
// prior estimate according to how much time has elapsed since the last
265266
// update. The inverse of the decay is used to scale the estimate towards the
266267
// observed RTT value.
267-
let elapsed = nanos(now - self.update_at);
268+
let elapsed = nanos(now.saturating_duration_since(self.update_at));
268269
let decay = (-elapsed / decay_ns).exp();
269270
let recency = 1.0 - decay;
270271
let next_estimate = (self.rtt_ns * decay) + (rtt * recency);

tower/src/retry/budget.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ impl Bucket {
175175
let mut gen = self.generation.lock().expect("generation lock");
176176

177177
let now = Instant::now();
178-
let diff = now - gen.time;
178+
let diff = now.saturating_duration_since(gen.time);
179179
if diff < self.window {
180180
// not expired yet
181181
return;

0 commit comments

Comments
 (0)