poison.rs - source
Description: Source of the Rust file `library/std/src/sync/poison.rs`.

#
std/sync/
poison.rs

    1//! Synchronization objects that employ poisoning.
    2//!
    3//! # Poisoning
    4//!
    5//! All synchronization objects in this module implement a strategy called
    6//! "poisoning" where a primitive becomes poisoned if it recognizes that some
    7//! thread has panicked while holding the exclusive access granted by the
    8//! primitive. This information is then propagated to all other threads
    9//! to signify that the data protected by this primitive is likely tainted
    10//! (some invariant is not being upheld).
    11//!
    12//! The specifics of how this "poisoned" state affects other threads and whether
    13//! the panics are recognized reliably or on a best-effort basis depend on the
    14//! primitive. See [Overview](#overview) below.
    15//!
    16//! The synchronization objects in this module have alternative implementations that do not employ
    17//! poisoning in the [`std::sync::nonpoison`] module.
    18//!
    19//! [`std::sync::nonpoison`]: crate::sync::nonpoison
    20//!
    21//! # Overview
    22//!
    23//! Below is a list of synchronization objects provided by this module
    24//! with a high-level overview for each object and a description
    25//! of how it employs "poisoning".
    26//!
    27//! - [`Condvar`]: Condition Variable, providing the ability to block
    28//!   a thread while waiting for an event to occur.
    29//!
    30//!   Condition variables are typically associated with
    31//!   a boolean predicate (a condition) and a mutex.
    32//!   This implementation is associated with [`poison::Mutex`](Mutex),
    33//!   which employs poisoning.
    34//!   For this reason, [`Condvar::wait()`] will return a [`LockResult`],
    35//!   just like [`poison::Mutex::lock()`](Mutex::lock) does.
    36//!
    37//! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at
    38//!   most one thread at a time is able to access some data.
    39//!
    40//!   Panicking while holding the lock typically poisons the mutex, but it is
    41//!   not guaranteed to detect this condition in all circumstances.
    42//!   [`Mutex::lock()`] returns a [`LockResult`], providing a way to deal with
    43//!   the poisoned state. See [`Mutex`'s documentation](Mutex#poisoning) for more.
    44//!
    45//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows
    46//!   multiple readers at the same time, while allowing only one
    47//!   writer at a time. In some cases, this can be more efficient than
    48//!   a mutex.
    49//!
    50//!   This implementation, like [`Mutex`], usually becomes poisoned on a panic.
    51//!   Note, however, that an `RwLock` may only be poisoned if a panic occurs
    52//!   while it is locked exclusively (write mode). If a panic occurs in any reader,
    53//!   then the lock will not be poisoned.
    54//!
    55//! Note that the [`Once`] type also employs poisoning, but since it has non-poisoning `force`
    56//! methods available on it, there is no separate `nonpoison` and `poison` version.
    57//!
    58//! [`Once`]: crate::sync::Once
    59
    60// If we are not unwinding, `PoisonError` is uninhabited.
    61#![cfg_attr(not(panic = "unwind"), expect(unreachable_code))]
    62
    63#[stable(feature = "rust1", since = "1.0.0")]
    64pub use self::condvar::Condvar;
    65#[unstable(feature = "mapped_lock_guards", issue = "117108")]
    66pub use self::mutex::MappedMutexGuard;
    67#[stable(feature = "rust1", since = "1.0.0")]
    68pub use self::mutex::{Mutex, MutexGuard};
    69#[unstable(feature = "mapped_lock_guards", issue = "117108")]
    70pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard};
    71#[stable(feature = "rust1", since = "1.0.0")]
    72pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
    73use crate::error::Error;
    74use crate::fmt;
    75#[cfg(panic = "unwind")]
    76use crate::sync::atomic::{Atomic, AtomicBool, Ordering};
    77#[cfg(panic = "unwind")]
    78use crate::thread;
    79
    80mod condvar;
    81#[stable(feature = "rust1", since = "1.0.0")]
    82mod mutex;
    83mod rwlock;
    84
    85pub(crate) struct Flag {
    86    #[cfg(panic = "unwind")]
    87    failed: Atomic<bool>,
    88}
    89
    90// Note that the Ordering uses to access the `failed` field of `Flag` below is
    91// always `Relaxed`, and that's because this isn't actually protecting any data,
    92// it's just a flag whether we've panicked or not.
    93//
    94// The actual location that this matters is when a mutex is **locked** which is
    95// where we have external synchronization ensuring that we see memory
    96// reads/writes to this flag.
    97//
    98// As a result, if it matters, we should see the correct value for `failed` in
    99// all cases.
    100
    101impl Flag {
    102    #[inline]
    103    pub const fn new() -> Flag {
    104        Flag {
    105            #[cfg(panic = "unwind")]
    106            failed: AtomicBool::new(false),
    107        }
    108    }
    109
    110    /// Checks the flag for an unguarded borrow, where we only care about existing poison.
    111    #[inline]
    112    pub fn borrow(&self) -> LockResult<()> {
    113        if self.get() { Err(PoisonError::new(())) } else { Ok(()) }
    114    }
    115
    116    /// Checks the flag for a guarded borrow, where we may also set poison when `done`.
    117    #[inline]
    118    pub fn guard(&self) -> LockResult<Guard> {
    119        let ret = Guard {
    120            #[cfg(panic = "unwind")]
    121            panicking: thread::panicking(),
    122        };
    123        if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) }
    124    }
    125
    126    #[inline]
    127    #[cfg(panic = "unwind")]
    128    pub fn done(&self, guard: &Guard) {
    129        if !guard.panicking && thread::panicking() {
    130            self.failed.store(true, Ordering::Relaxed);
    131        }
    132    }
    133
    134    #[inline]
    135    #[cfg(not(panic = "unwind"))]
    136    pub fn done(&self, _guard: &Guard) {}
    137
    138    #[inline]
    139    #[cfg(panic = "unwind")]
    140    pub fn get(&self) -> bool {
    141        self.failed.load(Ordering::Relaxed)
    142    }
    143
    144    #[inline(always)]
    145    #[cfg(not(panic = "unwind"))]
    146    pub fn get(&self) -> bool {
    147        false
    148    }
    149
    150    #[inline]
    151    pub fn clear(&self) {
    152        #[cfg(panic = "unwind")]
    153        self.failed.store(false, Ordering::Relaxed)
    154    }
    155}
    156
    157#[derive(Clone)]
    158pub(crate) struct Guard {
    159    #[cfg(panic = "unwind")]
    160    panicking: bool,
    161}
    162
    163/// A type of error which can be returned whenever a lock is acquired.
    164///
    165/// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock
    166/// is held. The precise semantics for when a lock is poisoned is documented on
    167/// each lock. For a lock in the poisoned state, unless the state is cleared manually,
    168/// all future acquisitions will return this error.
    169///
    170/// # Examples
    171///
    172/// ```
    173/// use std::sync::{Arc, Mutex};
    174/// use std::thread;
    175///
    176/// let mutex = Arc::new(Mutex::new(1));
    177///
    178/// // poison the mutex
    179/// let c_mutex = Arc::clone(&mutex);
    180/// let _ = thread::spawn(move || {
    181///     let mut data = c_mutex.lock().unwrap();
    182///     *data = 2;
    183///     panic!();
    184/// }).join();
    185///
    186/// match mutex.lock() {
    187///     Ok(_) => unreachable!(),
    188///     Err(p_err) => {
    189///         let data = p_err.get_ref();
    190///         println!("recovered: {data}");
    191///     }
    192/// };
    193/// ```
    194/// [`Mutex`]: crate::sync::Mutex
    195/// [`RwLock`]: crate::sync::RwLock
    196#[stable(feature = "rust1", since = "1.0.0")]
    197pub struct PoisonError<T> {
    198    data: T,
    199    #[cfg(not(panic = "unwind"))]
    200    _never: !,
    201}
    202
    203/// An enumeration of possible errors associated with a [`TryLockResult`] which
    204/// can occur while trying to acquire a lock, from the [`try_lock`] method on a
    205/// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`].
    206///
    207/// [`try_lock`]: crate::sync::Mutex::try_lock
    208/// [`try_read`]: crate::sync::RwLock::try_read
    209/// [`try_write`]: crate::sync::RwLock::try_write
    210/// [`Mutex`]: crate::sync::Mutex
    211/// [`RwLock`]: crate::sync::RwLock
    212#[stable(feature = "rust1", since = "1.0.0")]
    213pub enum TryLockError<T> {
    214    /// The lock could not be acquired because another thread failed while holding
    215    /// the lock.
    216    #[stable(feature = "rust1", since = "1.0.0")]
    217    Poisoned(#[stable(feature = "rust1", since = "1.0.0")] PoisonError<T>),
    218    /// The lock could not be acquired at this time because the operation would
    219    /// otherwise block.
    220    #[stable(feature = "rust1", since = "1.0.0")]
    221    WouldBlock,
    222}
    223
    224/// A type alias for the result of a lock method which can be poisoned.
    225///
    226/// The [`Ok`] variant of this result indicates that the primitive was not
    227/// poisoned, and the operation result is contained within. The [`Err`] variant indicates
    228/// that the primitive was poisoned. Note that the [`Err`] variant *also* carries
    229/// an associated value assigned by the lock method, and it can be acquired through the
    230/// [`into_inner`] method. The semantics of the associated value depends on the corresponding
    231/// lock method.
    232///
    233/// [`into_inner`]: PoisonError::into_inner
    234#[stable(feature = "rust1", since = "1.0.0")]
    235pub type LockResult<T> = Result<T, PoisonError<T>>;
    236
    237/// A type alias for the result of a nonblocking locking method.
    238///
    239/// For more information, see [`LockResult`]. A `TryLockResult` doesn't
    240/// necessarily hold the associated guard in the [`Err`] type as the lock might not
    241/// have been acquired for other reasons.
    242#[stable(feature = "rust1", since = "1.0.0")]
    243pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
    244
    245#[stable(feature = "rust1", since = "1.0.0")]
    246impl<T> fmt::Debug for PoisonError<T> {
    247    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    248        f.debug_struct("PoisonError").finish_non_exhaustive()
    249    }
    250}
    251
    252#[stable(feature = "rust1", since = "1.0.0")]
    253impl<T> fmt::Display for PoisonError<T> {
    254    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    255        "poisoned lock: another task failed inside".fmt(f)
    256    }
    257}
    258
    259#[stable(feature = "rust1", since = "1.0.0")]
    260impl<T> Error for PoisonError<T> {}
    261
    262impl<T> PoisonError<T> {
    263    /// Creates a `PoisonError`.
    264    ///
    265    /// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock)
    266    /// or [`RwLock::read`](crate::sync::RwLock::read).
    267    ///
    268    /// This method may panic if std was built with `panic="abort"`.
    269    #[cfg(panic = "unwind")]
    270    #[stable(feature = "sync_poison", since = "1.2.0")]
    271    pub fn new(data: T) -> PoisonError<T> {
    272        PoisonError { data }
    273    }
    274
    275    /// Creates a `PoisonError`.
    276    ///
    277    /// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock)
    278    /// or [`RwLock::read`](crate::sync::RwLock::read).
    279    ///
    280    /// This method may panic if std was built with `panic="abort"`.
    281    #[cfg(not(panic = "unwind"))]
    282    #[stable(feature = "sync_poison", since = "1.2.0")]
    283    #[track_caller]
    284    pub fn new(_data: T) -> PoisonError<T> {
    285        panic!("PoisonError created in a libstd built with panic=\"abort\"")
    286    }
    287
    288    /// Consumes this error indicating that a lock is poisoned, returning the
    289    /// associated data.
    290    ///
    291    /// # Examples
    292    ///
    293    /// ```
    294    /// use std::collections::HashSet;
    295    /// use std::sync::{Arc, Mutex};
    296    /// use std::thread;
    297    ///
    298    /// let mutex = Arc::new(Mutex::new(HashSet::new()));
    299    ///
    300    /// // poison the mutex
    301    /// let c_mutex = Arc::clone(&mutex);
    302    /// let _ = thread::spawn(move || {
    303    ///     let mut data = c_mutex.lock().unwrap();
    304    ///     data.insert(10);
    305    ///     panic!();
    306    /// }).join();
    307    ///
    308    /// let p_err = mutex.lock().unwrap_err();
    309    /// let data = p_err.into_inner();
    310    /// println!("recovered {} items", data.len());
    311    /// ```
    312    #[stable(feature = "sync_poison", since = "1.2.0")]
    313    pub fn into_inner(self) -> T {
    314        self.data
    315    }
    316
    317    /// Reaches into this error indicating that a lock is poisoned, returning a
    318    /// reference to the associated data.
    319    #[stable(feature = "sync_poison", since = "1.2.0")]
    320    pub fn get_ref(&self) -> &T {
    321        &self.data
    322    }
    323
    324    /// Reaches into this error indicating that a lock is poisoned, returning a
    325    /// mutable reference to the associated data.
    326    #[stable(feature = "sync_poison", since = "1.2.0")]
    327    pub fn get_mut(&mut self) -> &mut T {
    328        &mut self.data
    329    }
    330}
    331
    332#[stable(feature = "rust1", since = "1.0.0")]
    333impl<T> From<PoisonError<T>> for TryLockError<T> {
    334    fn from(err: PoisonError<T>) -> TryLockError<T> {
    335        TryLockError::Poisoned(err)
    336    }
    337}
    338
    339#[stable(feature = "rust1", since = "1.0.0")]
    340impl<T> fmt::Debug for TryLockError<T> {
    341    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    342        match *self {
    343            #[cfg(panic = "unwind")]
    344            TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f),
    345            #[cfg(not(panic = "unwind"))]
    346            TryLockError::Poisoned(ref p) => match p._never {},
    347            TryLockError::WouldBlock => "WouldBlock".fmt(f),
    348        }
    349    }
    350}
    351
    352#[stable(feature = "rust1", since = "1.0.0")]
    353impl<T> fmt::Display for TryLockError<T> {
    354    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    355        match *self {
    356            #[cfg(panic = "unwind")]
    357            TryLockError::Poisoned(..) => "poisoned lock: another task failed inside",
    358            #[cfg(not(panic = "unwind"))]
    359            TryLockError::Poisoned(ref p) => match p._never {},
    360            TryLockError::WouldBlock => "try_lock failed because the operation would block",
    361        }
    362        .fmt(f)
    363    }
    364}
    365
    366#[stable(feature = "rust1", since = "1.0.0")]
    367impl<T> Error for TryLockError<T> {
    368    #[allow(deprecated)]
    369    fn cause(&self) -> Option<&dyn Error> {
    370        match *self {
    371            #[cfg(panic = "unwind")]
    372            TryLockError::Poisoned(ref p) => Some(p),
    373            #[cfg(not(panic = "unwind"))]
    374            TryLockError::Poisoned(ref p) => match p._never {},
    375            _ => None,
    376        }
    377    }
    378}
    379
    380pub(crate) fn map_result<T, U, F>(result: LockResult<T>, f: F) -> LockResult<U>
    381where
    382    F: FnOnce(T) -> U,
    383{
    384    match result {
    385        Ok(t) => Ok(f(t)),
    386        #[cfg(panic = "unwind")]
    387        Err(PoisonError { data }) => Err(PoisonError::new(f(data))),
    388    }
    389}