Skip to content

Commit 36a199f

Browse files
andrei-21jkczyz
andcommitted
Use MonotonicTime as Instant shifted by 10 years forward
Such implementation allows `MonotonicTime` to go backward up to 10 years on all platforms. On some platforms (e.g. iOS) `Instant` is represented as `u64` of nanoseconds since the boot of the system. Obviously such implementation does not allow to go backward before the time of the boot. Co-authored-by: Andrei <[email protected]> Co-authored-by: Jeffrey Czyz <[email protected]>
1 parent fb140b5 commit 36a199f

File tree

2 files changed

+54
-2
lines changed

2 files changed

+54
-2
lines changed

lightning/src/routing/scoring.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ impl ReadableArgs<u64> for FixedPenaltyScorer {
325325
}
326326

327327
#[cfg(not(feature = "no-std"))]
328-
type ConfiguredTime = std::time::Instant;
328+
type ConfiguredTime = crate::util::time::MonotonicTime;
329329
#[cfg(feature = "no-std")]
330330
use crate::util::time::Eternity;
331331
#[cfg(feature = "no-std")]

lightning/src/util/time.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,51 @@ impl Sub<Duration> for Eternity {
5858
}
5959
}
6060

61+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
62+
#[cfg(not(feature = "no-std"))]
63+
pub struct MonotonicTime(std::time::Instant);
64+
65+
// The amount of time to shift `Instant` forward to prevent overflow when subtracting a `Duration`
66+
// from `Instant::now` on some operating systems (e.g., iOS representing `Instance` as `u64`).
67+
#[cfg(not(feature = "no-std"))]
68+
static SHIFT: Duration = Duration::from_secs(10 * 365 * 24 * 60 * 60); // 10 years.
69+
70+
#[cfg(not(feature = "no-std"))]
71+
impl Time for MonotonicTime {
72+
fn now() -> Self {
73+
let instant = std::time::Instant::now().checked_add(SHIFT).expect("Overflow on MonotonicTime instantiation");
74+
Self(instant)
75+
}
76+
77+
fn duration_since(&self, earlier: Self) -> Duration {
78+
// On rust prior to 1.60 `Instant::duration_since` will panic if time goes backwards.
79+
// However, we support rust versions prior to 1.60 and some users appear to have "monotonic
80+
// clocks" that go backwards in practice (likely relatively ancient kernels/etc). Thus, we
81+
// manually check for time going backwards here and return a duration of zero in that case.
82+
let now = Self::now();
83+
if now.0 > earlier.0 { now.0 - earlier.0 } else { Duration::from_secs(0) }
84+
}
85+
86+
fn duration_since_epoch() -> Duration {
87+
use std::time::SystemTime;
88+
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap()
89+
}
90+
91+
fn elapsed(&self) -> Duration {
92+
self.0.elapsed()
93+
}
94+
}
95+
96+
#[cfg(not(feature = "no-std"))]
97+
impl Sub<Duration> for MonotonicTime {
98+
type Output = Self;
99+
100+
fn sub(self, other: Duration) -> Self {
101+
let instant = self.0.checked_sub(other).expect("MonotonicTime is not supposed to go backward futher than 10 years");
102+
Self(instant)
103+
}
104+
}
105+
61106
#[cfg(not(feature = "no-std"))]
62107
impl Time for std::time::Instant {
63108
fn now() -> Self {
@@ -84,7 +129,7 @@ impl Time for std::time::Instant {
84129

85130
#[cfg(test)]
86131
pub mod tests {
87-
use super::{Time, Eternity};
132+
use super::{Time, Eternity, MonotonicTime};
88133

89134
use core::time::Duration;
90135
use core::ops::Sub;
@@ -154,4 +199,11 @@ pub mod tests {
154199
assert_eq!(now.elapsed(), Duration::from_secs(0));
155200
assert_eq!(later - elapsed, now);
156201
}
202+
203+
#[test]
204+
fn monotonic_time_go_backward() {
205+
let now = MonotonicTime::now();
206+
let ten_years = Duration::from_secs(10 * 365 * 24 * 60 * 60);
207+
let _past = now - ten_years;
208+
}
157209
}

0 commit comments

Comments
 (0)