diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 82cc13a74b7f1..04d5e1c51f540 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -33,7 +33,9 @@ use crate::sync::{Condvar, Mutex}; /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Barrier { + // FIXME(sync_nonpoison): switch to nonpoison version once it is available lock: Mutex, + // FIXME(sync_nonpoison): switch to nonpoison version once it is available cvar: Condvar, num_threads: usize, } diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 40510f5613450..1e4f9b79e0f4a 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -1,4 +1,4 @@ -use super::once::ExclusiveState; +use super::poison::once::ExclusiveState; use crate::cell::UnsafeCell; use crate::mem::ManuallyDrop; use crate::ops::Deref; @@ -63,6 +63,7 @@ union Data { /// ``` #[stable(feature = "lazy_cell", since = "1.80.0")] pub struct LazyLock T> { + // FIXME(nonpoison_once): if possible, switch to nonpoison version once it is available once: Once, data: UnsafeCell>, } diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 0fb77331293fe..92583f2aa2d4d 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -167,6 +167,11 @@ #![stable(feature = "rust1", since = "1.0.0")] +// No formatting: this file is just re-exports, and their order is worth preserving. +#![cfg_attr(rustfmt, rustfmt::skip)] + + +// These come from `core` & `alloc` and only in one flavor: no poisoning. #[unstable(feature = "exclusive_wrapper", issue = "98407")] pub use core::sync::Exclusive; #[stable(feature = "rust1", since = "1.0.0")] @@ -175,40 +180,63 @@ pub use core::sync::atomic; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::sync::{Arc, Weak}; +// FIXME(sync_nonpoison,sync_poison_mod): remove `#[doc(inline)]` below once the modules are stabilized. + +// These exist only in one flavor: no poisoning. +// FIXME(sync_nonpoison): should we move these to `sync::nonpoison` and unconditionally reexport them here? #[stable(feature = "rust1", since = "1.0.0")] pub use self::barrier::{Barrier, BarrierWaitResult}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::condvar::{Condvar, WaitTimeoutResult}; #[stable(feature = "lazy_cell", since = "1.80.0")] pub use self::lazy_lock::LazyLock; -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -pub use self::mutex::MappedMutexGuard; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::mutex::{Mutex, MutexGuard}; -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] -pub use self::once::{ONCE_INIT, Once, OnceState}; #[stable(feature = "once_cell", since = "1.70.0")] pub use self::once_lock::OnceLock; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::poison::{LockResult, PoisonError, TryLockError, TryLockResult}; +// FIXME(reentrant_lock): if poisoning support is added, move the implementation to `sync::poison`. +// Otherwise, keep together with `Barrier`, `LazyLock`, & `OnceLock`. #[unstable(feature = "reentrant_lock", issue = "121440")] pub use self::reentrant_lock::{ReentrantLock, ReentrantLockGuard}; -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; + + +// These make sense and exist only with poisoning. +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] +pub use self::poison::{LockResult, PoisonError}; + + +// These (should) exist in both flavors: with and without poisoning. +// FIXME(sync_nonpoison): implement nonpoison versions: +// * Mutex (nonpoison_mutex) +// * Condvar (nonpoison_condvar) +// * Once (nonpoison_once) +// * RwLock (nonpoison_rwlock) + +// FIXME(sync_nonpoison): conditionally select the default flavor based on edition(?). +use self::poison as default_poison_flavor; + +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] +pub use default_poison_flavor::{ + Mutex, MutexGuard, TryLockError, TryLockResult, + Condvar, WaitTimeoutResult, + Once, OnceState, + RwLock, RwLockReadGuard, RwLockWriteGuard, +}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +#[doc(inline)] +#[expect(deprecated)] +pub use default_poison_flavor::ONCE_INIT; +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +#[doc(inline)] +pub use default_poison_flavor::{MappedMutexGuard, MappedRwLockReadGuard, MappedRwLockWriteGuard}; + #[unstable(feature = "mpmc_channel", issue = "126840")] pub mod mpmc; pub mod mpsc; +#[unstable(feature = "sync_poison_mod", issue = "134646")] +pub mod poison; + mod barrier; -mod condvar; mod lazy_lock; -mod mutex; -pub(crate) mod once; mod once_lock; -mod poison; mod reentrant_lock; -mod rwlock; diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 0ae3cf4df3614..7be940b57e26e 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -101,6 +101,7 @@ use crate::sync::Once; /// ``` #[stable(feature = "once_cell", since = "1.70.0")] pub struct OnceLock { + // FIXME(sync_nonpoison): if possible, switch to nonpoison version once it is available once: Once, // Whether or not the value is initialized is tracked by `once.is_completed()`. value: UnsafeCell>, diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 9eb900c210350..1b8809734b8a8 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -1,3 +1,78 @@ +//! Synchronization objects that employ poisoning. +//! +//! # Poisoning +//! +//! All synchronization objects in this module implement a strategy called "poisoning" +//! where if a thread panics while holding the exclusive access granted by the primitive, +//! the state of the primitive is set to "poisoned". +//! This information is then propagated to all other threads +//! to signify that the data protected by this primitive is likely tainted +//! (some invariant is not being upheld). +//! +//! The specifics of how this "poisoned" state affects other threads +//! depend on the primitive. See [#Overview] bellow. +//! +//! For the alternative implementations that do not employ poisoning, +//! see `std::sys::nonpoisoning`. +//! +//! # Overview +//! +//! Below is a list of synchronization objects provided by this module +//! with a high-level overview for each object and a description +//! of how it employs "poisoning". +//! +//! - [`Condvar`]: Condition Variable, providing the ability to block +//! a thread while waiting for an event to occur. +//! +//! Condition variables are typically associated with +//! a boolean predicate (a condition) and a mutex. +//! This implementation is associated with [`poison::Mutex`](Mutex), +//! which employs poisoning. +//! For this reason, [`Condvar::wait()`] will return a [`LockResult`], +//! just like [`poison::Mutex::lock()`](Mutex::lock) does. +//! +//! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at +//! most one thread at a time is able to access some data. +//! +//! [`Mutex::lock()`] returns a [`LockResult`], +//! providing a way to deal with the poisoned state. +//! See [`Mutex`'s documentation](Mutex#poisoning) for more. +//! +//! - [`Once`]: A thread-safe way to run a piece of code only once. +//! Mostly useful for implementing one-time global initialization. +//! +//! [`Once`] is poisoned if the piece of code passed to +//! [`Once::call_once()`] or [`Once::call_once_force()`] panics. +//! When in poisoned state, subsequent calls to [`Once::call_once()`] will panic too. +//! [`Once::call_once_force()`] can be used to clear the poisoned state. +//! +//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows +//! multiple readers at the same time, while allowing only one +//! writer at a time. In some cases, this can be more efficient than +//! a mutex. +//! +//! This implementation, like [`Mutex`], will become poisoned on a panic. +//! Note, however, that an `RwLock` may only be poisoned if a panic occurs +//! while it is locked exclusively (write mode). If a panic occurs in any reader, +//! then the lock will not be poisoned. + +// FIXME(sync_nonpoison) add links to sync::nonpoison to the doc comment above. + +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::condvar::{Condvar, WaitTimeoutResult}; +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +pub use self::mutex::MappedMutexGuard; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::mutex::{Mutex, MutexGuard}; +#[stable(feature = "rust1", since = "1.0.0")] +#[expect(deprecated)] +pub use self::once::ONCE_INIT; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::once::{Once, OnceState}; +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::error::Error; use crate::fmt; #[cfg(panic = "unwind")] @@ -5,7 +80,13 @@ use crate::sync::atomic::{AtomicBool, Ordering}; #[cfg(panic = "unwind")] use crate::thread; -pub struct Flag { +mod condvar; +#[stable(feature = "rust1", since = "1.0.0")] +mod mutex; +pub(crate) mod once; +mod rwlock; + +pub(crate) struct Flag { #[cfg(panic = "unwind")] failed: AtomicBool, } @@ -78,7 +159,7 @@ impl Flag { } #[derive(Clone)] -pub struct Guard { +pub(crate) struct Guard { #[cfg(panic = "unwind")] panicking: bool, } @@ -316,7 +397,7 @@ impl Error for TryLockError { } } -pub fn map_result(result: LockResult, f: F) -> LockResult +pub(crate) fn map_result(result: LockResult, f: F) -> LockResult where F: FnOnce(T) -> U, { diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/poison/condvar.rs similarity index 99% rename from library/std/src/sync/condvar.rs rename to library/std/src/sync/poison/condvar.rs index 44ffcb528d937..a6e2389c93baf 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/poison/condvar.rs @@ -2,7 +2,7 @@ mod tests; use crate::fmt; -use crate::sync::{LockResult, MutexGuard, PoisonError, mutex, poison}; +use crate::sync::poison::{self, LockResult, MutexGuard, PoisonError, mutex}; use crate::sys::sync as sys; use crate::time::{Duration, Instant}; @@ -16,6 +16,8 @@ use crate::time::{Duration, Instant}; #[stable(feature = "wait_timeout", since = "1.5.0")] pub struct WaitTimeoutResult(bool); +// FIXME(sync_nonpoison): `WaitTimeoutResult` is actually poisoning-agnostic, it seems. +// Should we take advantage of this fact? impl WaitTimeoutResult { /// Returns `true` if the wait was known to have timed out. /// diff --git a/library/std/src/sync/condvar/tests.rs b/library/std/src/sync/poison/condvar/tests.rs similarity index 100% rename from library/std/src/sync/condvar/tests.rs rename to library/std/src/sync/poison/condvar/tests.rs diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/poison/mutex.rs similarity index 100% rename from library/std/src/sync/mutex.rs rename to library/std/src/sync/poison/mutex.rs diff --git a/library/std/src/sync/mutex/tests.rs b/library/std/src/sync/poison/mutex/tests.rs similarity index 100% rename from library/std/src/sync/mutex/tests.rs rename to library/std/src/sync/poison/mutex/tests.rs diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/poison/once.rs similarity index 100% rename from library/std/src/sync/once.rs rename to library/std/src/sync/poison/once.rs diff --git a/library/std/src/sync/once/tests.rs b/library/std/src/sync/poison/once/tests.rs similarity index 100% rename from library/std/src/sync/once/tests.rs rename to library/std/src/sync/poison/once/tests.rs diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/poison/rwlock.rs similarity index 100% rename from library/std/src/sync/rwlock.rs rename to library/std/src/sync/poison/rwlock.rs diff --git a/library/std/src/sync/rwlock/tests.rs b/library/std/src/sync/poison/rwlock/tests.rs similarity index 100% rename from library/std/src/sync/rwlock/tests.rs rename to library/std/src/sync/poison/rwlock/tests.rs diff --git a/library/std/src/sys/sync/once/futex.rs b/library/std/src/sys/sync/once/futex.rs index 10bfa81a6d72a..539f0fe89eaaa 100644 --- a/library/std/src/sys/sync/once/futex.rs +++ b/library/std/src/sys/sync/once/futex.rs @@ -1,7 +1,7 @@ use crate::cell::Cell; use crate::sync as public; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::once::ExclusiveState; +use crate::sync::poison::once::ExclusiveState; use crate::sys::futex::{Futex, Primitive, futex_wait, futex_wake_all}; // On some platforms, the OS is very nice and handles the waiter queue for us. diff --git a/library/std/src/sys/sync/once/no_threads.rs b/library/std/src/sys/sync/once/no_threads.rs index 88a1d50361ee4..2568059cfe3a8 100644 --- a/library/std/src/sys/sync/once/no_threads.rs +++ b/library/std/src/sys/sync/once/no_threads.rs @@ -1,6 +1,6 @@ use crate::cell::Cell; use crate::sync as public; -use crate::sync::once::ExclusiveState; +use crate::sync::poison::once::ExclusiveState; pub struct Once { state: Cell, diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index 5beff4ce6836c..fde1e0ca51029 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -58,7 +58,7 @@ use crate::cell::Cell; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Release}; use crate::sync::atomic::{AtomicBool, AtomicPtr}; -use crate::sync::once::ExclusiveState; +use crate::sync::poison::once::ExclusiveState; use crate::thread::{self, Thread}; use crate::{fmt, ptr, sync as public};