@@ -12,14 +12,19 @@ pub mod inner {
12
12
use crate :: sys:: time;
13
13
use crate :: time:: Duration ;
14
14
15
- const ZERO : time:: Instant = time:: Instant :: zero ( ) ;
15
+ pub ( in crate :: time ) const ZERO : time:: Instant = time:: Instant :: zero ( ) ;
16
16
17
17
// bits 30 and 31 are never used since the seconds part never exceeds 10^9
18
- const UNINITIALIZED : u64 = 0xff00_0000 ;
18
+ const UNINITIALIZED : u64 = 0b11 << 30 ;
19
19
static MONO : AtomicU64 = AtomicU64 :: new ( UNINITIALIZED ) ;
20
20
21
21
#[ inline]
22
22
pub ( super ) fn monotonize ( raw : time:: Instant ) -> time:: Instant {
23
+ monotonize_impl ( & MONO , raw)
24
+ }
25
+
26
+ #[ inline]
27
+ pub ( in crate :: time) fn monotonize_impl ( mono : & AtomicU64 , raw : time:: Instant ) -> time:: Instant {
23
28
let delta = raw. checked_sub_instant ( & ZERO ) . unwrap ( ) ;
24
29
let secs = delta. as_secs ( ) ;
25
30
// occupies no more than 30 bits (10^9 seconds)
@@ -32,16 +37,33 @@ pub mod inner {
32
37
// This could be a problem for programs that call instants at intervals greater
33
38
// than 68 years. Interstellar probes may want to ensure that actually_monotonic() is true.
34
39
let packed = ( secs << 32 ) | nanos;
35
- let old = MONO . load ( Relaxed ) ;
40
+ let old = mono . load ( Relaxed ) ;
36
41
37
42
if old == UNINITIALIZED || packed. wrapping_sub ( old) < u64:: MAX / 2 {
38
- MONO . store ( packed, Relaxed ) ;
43
+ mono . store ( packed, Relaxed ) ;
39
44
raw
40
45
} else {
41
- // Backslide occurred. We reconstruct monotonized time by assuming the clock will never
42
- // backslide more than 2`32 seconds which means we can reuse the upper 32bits from
43
- // the seconds.
44
- let secs = ( secs & 0xffff_ffff_0000_0000 ) | old >> 32 ;
46
+ // Backslide occurred. We reconstruct monotonized time from the upper 32 bit of the
47
+ // passed in value and the 64bits loaded from the atomic
48
+ let seconds_lower = old >> 32 ;
49
+ let mut seconds_upper = secs & 0xffff_ffff_0000_0000 ;
50
+ if secs & 0xffff_ffff > seconds_lower {
51
+ // Backslide caused the lower 32bit of the seconds part to wrap.
52
+ // This must be the case because the seconds part is larger even though
53
+ // we are in the backslide branch, i.e. the seconds count should be smaller or equal.
54
+ //
55
+ // We assume that backslides are smaller than 2^32 seconds
56
+ // which means we need to add 1 to the upper half to restore it.
57
+ //
58
+ // Example:
59
+ // most recent observed time: 0xA1_0000_0000_0000_0000u128
60
+ // bits stored in AtomicU64: 0x0000_0000_0000_0000u64
61
+ // backslide by 1s
62
+ // caller time is 0xA0_ffff_ffff_0000_0000u128
63
+ // -> we can fix up the upper half time by adding 1 << 32
64
+ seconds_upper = seconds_upper. wrapping_add ( 0x1_0000_0000 ) ;
65
+ }
66
+ let secs = seconds_upper | seconds_lower;
45
67
let nanos = old as u32 ;
46
68
ZERO . checked_add_duration ( & Duration :: new ( secs, nanos) ) . unwrap ( )
47
69
}
0 commit comments