Skip to content

Commit 2b512cc

Browse files
committed
fix potential race in AtomicU64 time monotonizer
1 parent 2b5ddf3 commit 2b512cc

File tree

1 file changed

+34
-28
lines changed

1 file changed

+34
-28
lines changed

library/std/src/time/monotonic.rs

+34-28
Original file line numberDiff line numberDiff line change
@@ -37,35 +37,41 @@ pub mod inner {
3737
// This could be a problem for programs that call instants at intervals greater
3838
// than 68 years. Interstellar probes may want to ensure that actually_monotonic() is true.
3939
let packed = (secs << 32) | nanos;
40-
let old = mono.load(Relaxed);
41-
42-
if old == UNINITIALIZED || packed.wrapping_sub(old) < u64::MAX / 2 {
43-
mono.store(packed, Relaxed);
44-
raw
45-
} else {
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);
40+
let mut old = mono.load(Relaxed);
41+
loop {
42+
if old == UNINITIALIZED || packed.wrapping_sub(old) < u64::MAX / 2 {
43+
match mono.compare_exchange_weak(old, packed, Relaxed, Relaxed) {
44+
Ok(_) => return raw,
45+
Err(x) => {
46+
old = x;
47+
continue;
48+
}
49+
}
50+
} else {
51+
// Backslide occurred. We reconstruct monotonized time from the upper 32 bit of the
52+
// passed in value and the 64bits loaded from the atomic
53+
let seconds_lower = old >> 32;
54+
let mut seconds_upper = secs & 0xffff_ffff_0000_0000;
55+
if secs & 0xffff_ffff > seconds_lower {
56+
// Backslide caused the lower 32bit of the seconds part to wrap.
57+
// This must be the case because the seconds part is larger even though
58+
// we are in the backslide branch, i.e. the seconds count should be smaller or equal.
59+
//
60+
// We assume that backslides are smaller than 2^32 seconds
61+
// which means we need to add 1 to the upper half to restore it.
62+
//
63+
// Example:
64+
// most recent observed time: 0xA1_0000_0000_0000_0000u128
65+
// bits stored in AtomicU64: 0x0000_0000_0000_0000u64
66+
// backslide by 1s
67+
// caller time is 0xA0_ffff_ffff_0000_0000u128
68+
// -> we can fix up the upper half time by adding 1 << 32
69+
seconds_upper = seconds_upper.wrapping_add(0x1_0000_0000);
70+
}
71+
let secs = seconds_upper | seconds_lower;
72+
let nanos = old as u32;
73+
return ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap();
6574
}
66-
let secs = seconds_upper | seconds_lower;
67-
let nanos = old as u32;
68-
ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap()
6975
}
7076
}
7177
}

0 commit comments

Comments
 (0)