about summary refs log tree commit diff
path: root/library/std/src/sys
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-10-15 10:45:36 +0000
committerbors <bors@rust-lang.org>2022-10-15 10:45:36 +0000
commitc93ef33700e4e4f84fd85690df71b14c1d2b0aa3 (patch)
tree40279eedaa7be0baa64437557b50667ac5bf4f2a /library/std/src/sys
parentb15e2c129e732fb3f1ff3c707d76341047a66af0 (diff)
parentac23c9fde9b5298d2151e374b890c91c342fb17e (diff)
downloadrust-c93ef33700e4e4f84fd85690df71b14c1d2b0aa3.tar.gz
rust-c93ef33700e4e4f84fd85690df71b14c1d2b0aa3.zip
Auto merge of #103083 - Dylan-DPC:rollup-97cvwdv, r=Dylan-DPC
Rollup of 6 pull requests

Successful merges:

 - #102773 (Use semaphores for thread parking on Apple platforms)
 - #102884 (resolve: Some cleanup, asserts and tests for lifetime ribs)
 - #102954 (Add missing checks for `doc(cfg_hide(...))`)
 - #102998 (Drop temporaries created in a condition, even if it's a let chain)
 - #103003 (Fix `suggest_floating_point_literal` ICE)
 - #103041 (Update cargo)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'library/std/src/sys')
-rw-r--r--library/std/src/sys/unix/thread_parker/darwin.rs131
-rw-r--r--library/std/src/sys/unix/thread_parker/mod.rs13
2 files changed, 143 insertions, 1 deletions
diff --git a/library/std/src/sys/unix/thread_parker/darwin.rs b/library/std/src/sys/unix/thread_parker/darwin.rs
new file mode 100644
index 00000000000..2f5356fe227
--- /dev/null
+++ b/library/std/src/sys/unix/thread_parker/darwin.rs
@@ -0,0 +1,131 @@
+//! Thread parking for Darwin-based systems.
+//!
+//! Darwin actually has futex syscalls (`__ulock_wait`/`__ulock_wake`), but they
+//! cannot be used in `std` because they are non-public (their use will lead to
+//! rejection from the App Store) and because they are only available starting
+//! with macOS version 10.12, even though the minimum target version is 10.7.
+//!
+//! Therefore, we need to look for other synchronization primitives. Luckily, Darwin
+//! supports semaphores, which allow us to implement the behaviour we need with
+//! only one primitive (as opposed to a mutex-condvar pair). We use the semaphore
+//! provided by libdispatch, as the underlying Mach semaphore is only dubiously
+//! public.
+
+use crate::pin::Pin;
+use crate::sync::atomic::{
+    AtomicI8,
+    Ordering::{Acquire, Release},
+};
+use crate::time::Duration;
+
+type dispatch_semaphore_t = *mut crate::ffi::c_void;
+type dispatch_time_t = u64;
+
+const DISPATCH_TIME_NOW: dispatch_time_t = 0;
+const DISPATCH_TIME_FOREVER: dispatch_time_t = !0;
+
+// Contained in libSystem.dylib, which is linked by default.
+extern "C" {
+    fn dispatch_time(when: dispatch_time_t, delta: i64) -> dispatch_time_t;
+    fn dispatch_semaphore_create(val: isize) -> dispatch_semaphore_t;
+    fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) -> isize;
+    fn dispatch_semaphore_signal(dsema: dispatch_semaphore_t) -> isize;
+    fn dispatch_release(object: *mut crate::ffi::c_void);
+}
+
+const EMPTY: i8 = 0;
+const NOTIFIED: i8 = 1;
+const PARKED: i8 = -1;
+
+pub struct Parker {
+    semaphore: dispatch_semaphore_t,
+    state: AtomicI8,
+}
+
+unsafe impl Sync for Parker {}
+unsafe impl Send for Parker {}
+
+impl Parker {
+    pub unsafe fn new(parker: *mut Parker) {
+        let semaphore = dispatch_semaphore_create(0);
+        assert!(
+            !semaphore.is_null(),
+            "failed to create dispatch semaphore for thread synchronization"
+        );
+        parker.write(Parker { semaphore, state: AtomicI8::new(EMPTY) })
+    }
+
+    // Does not need `Pin`, but other implementation do.
+    pub unsafe fn park(self: Pin<&Self>) {
+        // The semaphore counter must be zero at this point, because unparking
+        // threads will not actually increase it until we signalled that we
+        // are waiting.
+
+        // Change NOTIFIED to EMPTY and EMPTY to PARKED.
+        if self.state.fetch_sub(1, Acquire) == NOTIFIED {
+            return;
+        }
+
+        // Another thread may increase the semaphore counter from this point on.
+        // If it is faster than us, we will decrement it again immediately below.
+        // If we are faster, we wait.
+
+        // Ensure that the semaphore counter has actually been decremented, even
+        // if the call timed out for some reason.
+        while dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) != 0 {}
+
+        // At this point, the semaphore counter is zero again.
+
+        // We were definitely woken up, so we don't need to check the state.
+        // Still, we need to reset the state using a swap to observe the state
+        // change with acquire ordering.
+        self.state.swap(EMPTY, Acquire);
+    }
+
+    // Does not need `Pin`, but other implementation do.
+    pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
+        if self.state.fetch_sub(1, Acquire) == NOTIFIED {
+            return;
+        }
+
+        let nanos = dur.as_nanos().try_into().unwrap_or(i64::MAX);
+        let timeout = dispatch_time(DISPATCH_TIME_NOW, nanos);
+
+        let timeout = dispatch_semaphore_wait(self.semaphore, timeout) != 0;
+
+        let state = self.state.swap(EMPTY, Acquire);
+        if state == NOTIFIED && timeout {
+            // If the state was NOTIFIED but semaphore_wait returned without
+            // decrementing the count because of a timeout, it means another
+            // thread is about to call semaphore_signal. We must wait for that
+            // to happen to ensure the semaphore count is reset.
+            while dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) != 0 {}
+        } else {
+            // Either a timeout occurred and we reset the state before any thread
+            // tried to wake us up, or we were woken up and reset the state,
+            // making sure to observe the state change with acquire ordering.
+            // Either way, the semaphore counter is now zero again.
+        }
+    }
+
+    // Does not need `Pin`, but other implementation do.
+    pub fn unpark(self: Pin<&Self>) {
+        let state = self.state.swap(NOTIFIED, Release);
+        if state == PARKED {
+            unsafe {
+                dispatch_semaphore_signal(self.semaphore);
+            }
+        }
+    }
+}
+
+impl Drop for Parker {
+    fn drop(&mut self) {
+        // SAFETY:
+        // We always ensure that the semaphore count is reset, so this will
+        // never cause an exception.
+        unsafe {
+            dispatch_release(self.semaphore);
+        }
+    }
+}
diff --git a/library/std/src/sys/unix/thread_parker/mod.rs b/library/std/src/sys/unix/thread_parker/mod.rs
index e2453580dc7..35f1e68a87e 100644
--- a/library/std/src/sys/unix/thread_parker/mod.rs
+++ b/library/std/src/sys/unix/thread_parker/mod.rs
@@ -11,7 +11,18 @@
 )))]
 
 cfg_if::cfg_if! {
-    if #[cfg(target_os = "netbsd")] {
+    if #[cfg(all(
+        any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "watchos",
+            target_os = "tvos",
+        ),
+        not(miri),
+    ))] {
+        mod darwin;
+        pub use darwin::Parker;
+    } else if #[cfg(target_os = "netbsd")] {
         mod netbsd;
         pub use netbsd::Parker;
     } else {