about summary refs log tree commit diff
path: root/library/std/src/sys/unix
diff options
context:
space:
mode:
authorThom Chiovoloni <chiovolonit@gmail.com>2020-10-08 14:34:11 -0700
committerThom Chiovoloni <chiovolonit@gmail.com>2020-10-08 14:34:11 -0700
commitf30cc74fb41916d11a27e6b29ebbe73298534573 (patch)
tree09922255f8a56339ed9517245f35c7f4d8142f85 /library/std/src/sys/unix
parentccea570488694e502c1a7ca8f3f0866c9b763ce4 (diff)
downloadrust-f30cc74fb41916d11a27e6b29ebbe73298534573.tar.gz
rust-f30cc74fb41916d11a27e6b29ebbe73298534573.zip
Avoid SeqCst or static mut in mach_timebase_info cache
Diffstat (limited to 'library/std/src/sys/unix')
-rw-r--r--library/std/src/sys/unix/time.rs58
1 files changed, 36 insertions, 22 deletions
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index f2a9cb5a0e8..5dc1ade9c1f 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -117,8 +117,7 @@ impl Hash for Timespec {
 #[cfg(any(target_os = "macos", target_os = "ios"))]
 mod inner {
     use crate::fmt;
-    use crate::mem;
-    use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
+    use crate::sync::atomic::{AtomicBool, AtomicU64, Ordering};
     use crate::sys::cvt;
     use crate::sys_common::mul_div_u64;
     use crate::time::Duration;
@@ -233,31 +232,46 @@ mod inner {
     }
 
     fn info() -> mach_timebase_info {
-        static mut INFO: mach_timebase_info = mach_timebase_info { numer: 0, denom: 0 };
-        static STATE: AtomicUsize = AtomicUsize::new(0);
+        static INITIALIZED: AtomicBool = AtomicBool::new(false);
+        static INFO_BITS: AtomicU64 = AtomicU64::new(0);
+
+        // If a previous thread has filled in this global INITIALIZED, use that.
+        if INITIALIZED.load(Ordering::Acquire) {
+            // The Acquire/Release pair used for INITIALIZED ensures that this
+            // 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 ...
+        extern "C" {
+            fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t;
+        }
 
+        let mut info = info_from_bits(0);
         unsafe {
-            // If a previous thread has filled in this global state, use that.
-            if STATE.load(SeqCst) == 2 {
-                return INFO;
-            }
+            mach_timebase_info(&mut info);
+        }
 
-            // ... otherwise learn for ourselves ...
-            let mut info = mem::zeroed();
-            extern "C" {
-                fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t;
-            }
+        // 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);
 
-            mach_timebase_info(&mut info);
+        // 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
+    }
 
-            // ... 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;
-        }
+    #[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 }
     }
 }