@@ -117,8 +117,7 @@ impl Hash for Timespec {
117
117
#[ cfg( any( target_os = "macos" , target_os = "ios" ) ) ]
118
118
mod inner {
119
119
use crate :: fmt;
120
- use crate :: mem;
121
- use crate :: sync:: atomic:: { AtomicUsize , Ordering :: SeqCst } ;
120
+ use crate :: sync:: atomic:: { AtomicBool , AtomicU64 , Ordering } ;
122
121
use crate :: sys:: cvt;
123
122
use crate :: sys_common:: mul_div_u64;
124
123
use crate :: time:: Duration ;
@@ -233,31 +232,46 @@ mod inner {
233
232
}
234
233
235
234
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
+ }
238
250
251
+ let mut info = info_from_bits ( 0 ) ;
239
252
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
+ }
244
255
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 ) ;
250
259
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
+ }
252
266
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 }
261
275
}
262
276
}
263
277
0 commit comments