diff options
| author | joboet <jonasboettiger@icloud.com> | 2024-04-23 11:49:37 +0200 |
|---|---|---|
| committer | joboet <jonasboettiger@icloud.com> | 2024-05-02 12:38:26 +0200 |
| commit | a56fd370fc3e6b5cfbd9eb0b96f533d0bcca0b1f (patch) | |
| tree | a1fc69d5c82808c6018e0500d1f77bafdf002b85 /library/std/src/sys_common | |
| parent | fcc06c894b17f4d0c80b8934ea5f27faa894c960 (diff) | |
std: move thread parking to `sys::sync`
Diffstat (limited to 'library/std/src/sys_common')
| -rw-r--r-- | library/std/src/sys_common/mod.rs | 1 | ||||
| -rw-r--r-- | library/std/src/sys_common/thread_parking/futex.rs | 97 | ||||
| -rw-r--r-- | library/std/src/sys_common/thread_parking/id.rs | 103 | ||||
| -rw-r--r-- | library/std/src/sys_common/thread_parking/mod.rs | 24 |
4 files changed, 0 insertions, 225 deletions
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index cc21560fff5..3a38ba1100f 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -26,7 +26,6 @@ pub mod io; pub mod lazy_box; pub mod process; pub mod thread_local_dtor; -pub mod thread_parking; pub mod wstr; pub mod wtf8; diff --git a/library/std/src/sys_common/thread_parking/futex.rs b/library/std/src/sys_common/thread_parking/futex.rs deleted file mode 100644 index 588e7b27826..00000000000 --- a/library/std/src/sys_common/thread_parking/futex.rs +++ /dev/null @@ -1,97 +0,0 @@ -use crate::pin::Pin; -use crate::sync::atomic::AtomicU32; -use crate::sync::atomic::Ordering::{Acquire, Release}; -use crate::sys::futex::{futex_wait, futex_wake}; -use crate::time::Duration; - -const PARKED: u32 = u32::MAX; -const EMPTY: u32 = 0; -const NOTIFIED: u32 = 1; - -pub struct Parker { - state: AtomicU32, -} - -// Notes about memory ordering: -// -// Memory ordering is only relevant for the relative ordering of operations -// between different variables. Even Ordering::Relaxed guarantees a -// monotonic/consistent order when looking at just a single atomic variable. -// -// So, since this parker is just a single atomic variable, we only need to look -// at the ordering guarantees we need to provide to the 'outside world'. -// -// The only memory ordering guarantee that parking and unparking provide, is -// that things which happened before unpark() are visible on the thread -// returning from park() afterwards. Otherwise, it was effectively unparked -// before unpark() was called while still consuming the 'token'. -// -// In other words, unpark() needs to synchronize with the part of park() that -// consumes the token and returns. -// -// This is done with a release-acquire synchronization, by using -// Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using -// Ordering::Acquire when checking for this state in park(). -impl Parker { - /// Construct the futex parker. The UNIX parker implementation - /// requires this to happen in-place. - pub unsafe fn new_in_place(parker: *mut Parker) { - parker.write(Self { state: AtomicU32::new(EMPTY) }); - } - - // Assumes this is only called by the thread that owns the Parker, - // which means that `self.state != PARKED`. - pub unsafe fn park(self: Pin<&Self>) { - // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the - // first case. - if self.state.fetch_sub(1, Acquire) == NOTIFIED { - return; - } - loop { - // Wait for something to happen, assuming it's still set to PARKED. - futex_wait(&self.state, PARKED, None); - // Change NOTIFIED=>EMPTY and return in that case. - if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() { - return; - } else { - // Spurious wake up. We loop to try again. - } - } - } - - // Assumes this is only called by the thread that owns the Parker, - // which means that `self.state != PARKED`. This implementation doesn't - // require `Pin`, but other implementations do. - pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) { - // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the - // first case. - if self.state.fetch_sub(1, Acquire) == NOTIFIED { - return; - } - // Wait for something to happen, assuming it's still set to PARKED. - futex_wait(&self.state, PARKED, Some(timeout)); - // This is not just a store, because we need to establish a - // release-acquire ordering with unpark(). - if self.state.swap(EMPTY, Acquire) == NOTIFIED { - // Woke up because of unpark(). - } else { - // Timeout or spurious wake up. - // We return either way, because we can't easily tell if it was the - // timeout or not. - } - } - - // This implementation doesn't require `Pin`, but other implementations do. - #[inline] - pub fn unpark(self: Pin<&Self>) { - // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and - // wake the thread in the first case. - // - // Note that even NOTIFIED=>NOTIFIED results in a write. This is on - // purpose, to make sure every unpark() has a release-acquire ordering - // with park(). - if self.state.swap(NOTIFIED, Release) == PARKED { - futex_wake(&self.state); - } - } -} diff --git a/library/std/src/sys_common/thread_parking/id.rs b/library/std/src/sys_common/thread_parking/id.rs deleted file mode 100644 index 04667439660..00000000000 --- a/library/std/src/sys_common/thread_parking/id.rs +++ /dev/null @@ -1,103 +0,0 @@ -//! Thread parking using thread ids. -//! -//! Some platforms (notably NetBSD) have thread parking primitives whose semantics -//! match those offered by `thread::park`, with the difference that the thread to -//! be unparked is referenced by a platform-specific thread id. Since the thread -//! parker is constructed before that id is known, an atomic state variable is used -//! to manage the park state and propagate the thread id. This also avoids platform -//! calls in the case where `unpark` is called before `park`. - -use crate::cell::UnsafeCell; -use crate::pin::Pin; -use crate::sync::atomic::{ - fence, AtomicI8, - Ordering::{Acquire, Relaxed, Release}, -}; -use crate::sys::thread_parking::{current, park, park_timeout, unpark, ThreadId}; -use crate::time::Duration; - -pub struct Parker { - state: AtomicI8, - tid: UnsafeCell<Option<ThreadId>>, -} - -const PARKED: i8 = -1; -const EMPTY: i8 = 0; -const NOTIFIED: i8 = 1; - -impl Parker { - pub fn new() -> Parker { - Parker { state: AtomicI8::new(EMPTY), tid: UnsafeCell::new(None) } - } - - /// Create a new thread parker. UNIX requires this to happen in-place. - pub unsafe fn new_in_place(parker: *mut Parker) { - parker.write(Parker::new()) - } - - /// # Safety - /// * must always be called from the same thread - /// * must be called before the state is set to PARKED - unsafe fn init_tid(&self) { - // The field is only ever written to from this thread, so we don't need - // synchronization to read it here. - if self.tid.get().read().is_none() { - // Because this point is only reached once, before the state is set - // to PARKED for the first time, the non-atomic write here can not - // conflict with reads by other threads. - self.tid.get().write(Some(current())); - // Ensure that the write can be observed by all threads reading the - // state. Synchronizes with the acquire barrier in `unpark`. - fence(Release); - } - } - - pub unsafe fn park(self: Pin<&Self>) { - self.init_tid(); - - // Changes NOTIFIED to EMPTY and EMPTY to PARKED. - let state = self.state.fetch_sub(1, Acquire); - if state == EMPTY { - // Loop to guard against spurious wakeups. - // The state must be reset with acquire ordering to ensure that all - // calls to `unpark` synchronize with this thread. - while self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed).is_err() { - park(self.state.as_ptr().addr()); - } - } - } - - pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) { - self.init_tid(); - - let state = self.state.fetch_sub(1, Acquire).wrapping_sub(1); - if state == PARKED { - park_timeout(dur, self.state.as_ptr().addr()); - // Swap to ensure that we observe all state changes with acquire - // ordering. - self.state.swap(EMPTY, Acquire); - } - } - - pub fn unpark(self: Pin<&Self>) { - let state = self.state.swap(NOTIFIED, Release); - if state == PARKED { - // Synchronize with the release fence in `init_tid` to observe the - // write to `tid`. - fence(Acquire); - // # Safety - // The thread id is initialized before the state is set to `PARKED` - // for the first time and is not written to from that point on - // (negating the need for an atomic read). - let tid = unsafe { self.tid.get().read().unwrap_unchecked() }; - // It is possible that the waiting thread woke up because of a timeout - // and terminated before this call is made. This call then returns an - // error or wakes up an unrelated thread. The platform API and - // environment does allow this, however. - unpark(tid, self.state.as_ptr().addr()); - } - } -} - -unsafe impl Send for Parker {} -unsafe impl Sync for Parker {} diff --git a/library/std/src/sys_common/thread_parking/mod.rs b/library/std/src/sys_common/thread_parking/mod.rs deleted file mode 100644 index c4d3f9ea2f4..00000000000 --- a/library/std/src/sys_common/thread_parking/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -cfg_if::cfg_if! { - if #[cfg(any( - target_os = "linux", - target_os = "android", - all(target_arch = "wasm32", target_feature = "atomics"), - target_os = "freebsd", - target_os = "openbsd", - target_os = "dragonfly", - target_os = "fuchsia", - target_os = "hermit", - ))] { - mod futex; - pub use futex::Parker; - } else if #[cfg(any( - target_os = "netbsd", - all(target_vendor = "fortanix", target_env = "sgx"), - target_os = "solid_asp3", - ))] { - mod id; - pub use id::Parker; - } else { - pub use crate::sys::thread_parking::Parker; - } -} |
