@@ -117,8 +117,7 @@ impl Hash for Timespec {
117117#[ cfg( any( target_os = "macos" , target_os = "ios" ) ) ]
118118mod 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