Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit f30cc74

Browse files
committed
Avoid SeqCst or static mut in mach_timebase_info cache
1 parent ccea570 commit f30cc74

File tree

1 file changed

+36
-22
lines changed

1 file changed

+36
-22
lines changed

library/std/src/sys/unix/time.rs

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,7 @@ impl Hash for Timespec {
117117
#[cfg(any(target_os = "macos", target_os = "ios"))]
118118
mod inner {
119119
use crate::fmt;
120-
use crate::mem;
121-
use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
120+
use crate::sync::atomic::{AtomicBool, AtomicU64, Ordering};
122121
use crate::sys::cvt;
123122
use crate::sys_common::mul_div_u64;
124123
use crate::time::Duration;
@@ -233,31 +232,46 @@ mod inner {
233232
}
234233

235234
fn info() -> mach_timebase_info {
236-
static mut INFO: mach_timebase_info = mach_timebase_info { numer: 0, denom: 0 };
237-
static STATE: AtomicUsize = AtomicUsize::new(0);
235+
static INITIALIZED: AtomicBool = AtomicBool::new(false);
236+
static INFO_BITS: AtomicU64 = AtomicU64::new(0);
237+
238+
// If a previous thread has filled in this global INITIALIZED, use that.
239+
if INITIALIZED.load(Ordering::Acquire) {
240+
// The Acquire/Release pair used for INITIALIZED ensures that this
241+
// load can see the corresponding `INFO_BITS` store, despite them
242+
// both being Relaxed.
243+
return info_from_bits(INFO_BITS.load(Ordering::Relaxed));
244+
}
245+
246+
// ... otherwise learn for ourselves ...
247+
extern "C" {
248+
fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t;
249+
}
238250

251+
let mut info = info_from_bits(0);
239252
unsafe {
240-
// If a previous thread has filled in this global state, use that.
241-
if STATE.load(SeqCst) == 2 {
242-
return INFO;
243-
}
253+
mach_timebase_info(&mut info);
254+
}
244255

245-
// ... otherwise learn for ourselves ...
246-
let mut info = mem::zeroed();
247-
extern "C" {
248-
fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t;
249-
}
256+
// Note: This is racy, but the race is against other threads trying to
257+
// write the same value.
258+
INFO_BITS.store(info_to_bits(info), Ordering::Relaxed);
250259

251-
mach_timebase_info(&mut info);
260+
// The `Release` here "publishes" the store of `INFO_BITS` to other
261+
// threads (which do a `INITIALIZED.load(Acquire)`) despite it being
262+
// read/written w/ `Relaxed`.
263+
INITIALIZED.store(true, Ordering::Release);
264+
info
265+
}
252266

253-
// ... and attempt to be the one thread that stores it globally for
254-
// all other threads
255-
if STATE.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() {
256-
INFO = info;
257-
STATE.store(2, SeqCst);
258-
}
259-
return info;
260-
}
267+
#[inline]
268+
fn info_to_bits(info: mach_timebase_info) -> u64 {
269+
((info.denom as u64) << 32) | (info.numer as u64)
270+
}
271+
272+
#[inline]
273+
fn info_from_bits(bits: u64) -> mach_timebase_info {
274+
mach_timebase_info { numer: bits as u32, denom: (bits >> 32) as u32 }
261275
}
262276
}
263277

0 commit comments

Comments
 (0)