Avoid SeqCst or static mut in mach_timebase_info cache
This commit is contained in:
parent
ccea570488
commit
f30cc74fb4
1 changed files with 39 additions and 25 deletions
|
@ -117,8 +117,7 @@ impl Hash for Timespec {
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
mod inner {
|
mod inner {
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::mem;
|
use crate::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||||
use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
|
||||||
use crate::sys::cvt;
|
use crate::sys::cvt;
|
||||||
use crate::sys_common::mul_div_u64;
|
use crate::sys_common::mul_div_u64;
|
||||||
use crate::time::Duration;
|
use crate::time::Duration;
|
||||||
|
@ -233,31 +232,46 @@ mod inner {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn info() -> mach_timebase_info {
|
fn info() -> mach_timebase_info {
|
||||||
static mut INFO: mach_timebase_info = mach_timebase_info { numer: 0, denom: 0 };
|
static INITIALIZED: AtomicBool = AtomicBool::new(false);
|
||||||
static STATE: AtomicUsize = AtomicUsize::new(0);
|
static INFO_BITS: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
unsafe {
|
// If a previous thread has filled in this global INITIALIZED, use that.
|
||||||
// If a previous thread has filled in this global state, use that.
|
if INITIALIZED.load(Ordering::Acquire) {
|
||||||
if STATE.load(SeqCst) == 2 {
|
// The Acquire/Release pair used for INITIALIZED ensures that this
|
||||||
return INFO;
|
// load can see the corresponding `INFO_BITS` store, despite them
|
||||||
}
|
// both being Relaxed.
|
||||||
|
return info_from_bits(INFO_BITS.load(Ordering::Relaxed));
|
||||||
// ... otherwise learn for ourselves ...
|
|
||||||
let mut info = mem::zeroed();
|
|
||||||
extern "C" {
|
|
||||||
fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
mach_timebase_info(&mut info);
|
|
||||||
|
|
||||||
// ... and attempt to be the one thread that stores it globally for
|
|
||||||
// all other threads
|
|
||||||
if STATE.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() {
|
|
||||||
INFO = info;
|
|
||||||
STATE.store(2, SeqCst);
|
|
||||||
}
|
|
||||||
return info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ... otherwise learn for ourselves ...
|
||||||
|
extern "C" {
|
||||||
|
fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut info = info_from_bits(0);
|
||||||
|
unsafe {
|
||||||
|
mach_timebase_info(&mut info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: This is racy, but the race is against other threads trying to
|
||||||
|
// write the same value.
|
||||||
|
INFO_BITS.store(info_to_bits(info), Ordering::Relaxed);
|
||||||
|
|
||||||
|
// The `Release` here "publishes" the store of `INFO_BITS` to other
|
||||||
|
// threads (which do a `INITIALIZED.load(Acquire)`) despite it being
|
||||||
|
// read/written w/ `Relaxed`.
|
||||||
|
INITIALIZED.store(true, Ordering::Release);
|
||||||
|
info
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn info_to_bits(info: mach_timebase_info) -> u64 {
|
||||||
|
((info.denom as u64) << 32) | (info.numer as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn info_from_bits(bits: u64) -> mach_timebase_info {
|
||||||
|
mach_timebase_info { numer: bits as u32, denom: (bits >> 32) as u32 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue