From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- library/std/src/sync/poison.rs | 272 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 library/std/src/sync/poison.rs (limited to 'library/std/src/sync/poison.rs') diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs new file mode 100644 index 000000000..741312d55 --- /dev/null +++ b/library/std/src/sync/poison.rs @@ -0,0 +1,272 @@ +use crate::error::Error; +use crate::fmt; +use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::thread; + +pub struct Flag { + failed: AtomicBool, +} + +// Note that the Ordering uses to access the `failed` field of `Flag` below is +// always `Relaxed`, and that's because this isn't actually protecting any data, +// it's just a flag whether we've panicked or not. +// +// The actual location that this matters is when a mutex is **locked** which is +// where we have external synchronization ensuring that we see memory +// reads/writes to this flag. +// +// As a result, if it matters, we should see the correct value for `failed` in +// all cases. + +impl Flag { + #[inline] + pub const fn new() -> Flag { + Flag { failed: AtomicBool::new(false) } + } + + /// Check the flag for an unguarded borrow, where we only care about existing poison. + #[inline] + pub fn borrow(&self) -> LockResult<()> { + if self.get() { Err(PoisonError::new(())) } else { Ok(()) } + } + + /// Check the flag for a guarded borrow, where we may also set poison when `done`. + #[inline] + pub fn guard(&self) -> LockResult { + let ret = Guard { panicking: thread::panicking() }; + if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) } + } + + #[inline] + pub fn done(&self, guard: &Guard) { + if !guard.panicking && thread::panicking() { + self.failed.store(true, Ordering::Relaxed); + } + } + + #[inline] + pub fn get(&self) -> bool { + self.failed.load(Ordering::Relaxed) + } + + #[inline] + pub fn clear(&self) { + self.failed.store(false, Ordering::Relaxed) + } +} + +pub struct Guard { + panicking: bool, +} + +/// A type of error which can be returned whenever a lock is acquired. +/// +/// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock +/// is held. The precise semantics for when a lock is poisoned is documented on +/// each lock, but once a lock is poisoned then all future acquisitions will +/// return this error. +/// +/// # Examples +/// +/// ``` +/// use std::sync::{Arc, Mutex}; +/// use std::thread; +/// +/// let mutex = Arc::new(Mutex::new(1)); +/// +/// // poison the mutex +/// let c_mutex = Arc::clone(&mutex); +/// let _ = thread::spawn(move || { +/// let mut data = c_mutex.lock().unwrap(); +/// *data = 2; +/// panic!(); +/// }).join(); +/// +/// match mutex.lock() { +/// Ok(_) => unreachable!(), +/// Err(p_err) => { +/// let data = p_err.get_ref(); +/// println!("recovered: {data}"); +/// } +/// }; +/// ``` +/// [`Mutex`]: crate::sync::Mutex +/// [`RwLock`]: crate::sync::RwLock +#[stable(feature = "rust1", since = "1.0.0")] +pub struct PoisonError { + guard: T, +} + +/// An enumeration of possible errors associated with a [`TryLockResult`] which +/// can occur while trying to acquire a lock, from the [`try_lock`] method on a +/// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`]. +/// +/// [`try_lock`]: crate::sync::Mutex::try_lock +/// [`try_read`]: crate::sync::RwLock::try_read +/// [`try_write`]: crate::sync::RwLock::try_write +/// [`Mutex`]: crate::sync::Mutex +/// [`RwLock`]: crate::sync::RwLock +#[stable(feature = "rust1", since = "1.0.0")] +pub enum TryLockError { + /// The lock could not be acquired because another thread failed while holding + /// the lock. + #[stable(feature = "rust1", since = "1.0.0")] + Poisoned(#[stable(feature = "rust1", since = "1.0.0")] PoisonError), + /// The lock could not be acquired at this time because the operation would + /// otherwise block. + #[stable(feature = "rust1", since = "1.0.0")] + WouldBlock, +} + +/// A type alias for the result of a lock method which can be poisoned. +/// +/// The [`Ok`] variant of this result indicates that the primitive was not +/// poisoned, and the `Guard` is contained within. The [`Err`] variant indicates +/// that the primitive was poisoned. Note that the [`Err`] variant *also* carries +/// the associated guard, and it can be acquired through the [`into_inner`] +/// method. +/// +/// [`into_inner`]: PoisonError::into_inner +#[stable(feature = "rust1", since = "1.0.0")] +pub type LockResult = Result>; + +/// A type alias for the result of a nonblocking locking method. +/// +/// For more information, see [`LockResult`]. A `TryLockResult` doesn't +/// necessarily hold the associated guard in the [`Err`] type as the lock might not +/// have been acquired for other reasons. +#[stable(feature = "rust1", since = "1.0.0")] +pub type TryLockResult = Result>; + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for PoisonError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PoisonError").finish_non_exhaustive() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for PoisonError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "poisoned lock: another task failed inside".fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for PoisonError { + #[allow(deprecated)] + fn description(&self) -> &str { + "poisoned lock: another task failed inside" + } +} + +impl PoisonError { + /// Creates a `PoisonError`. + /// + /// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock) + /// or [`RwLock::read`](crate::sync::RwLock::read). + #[stable(feature = "sync_poison", since = "1.2.0")] + pub fn new(guard: T) -> PoisonError { + PoisonError { guard } + } + + /// Consumes this error indicating that a lock is poisoned, returning the + /// underlying guard to allow access regardless. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// use std::sync::{Arc, Mutex}; + /// use std::thread; + /// + /// let mutex = Arc::new(Mutex::new(HashSet::new())); + /// + /// // poison the mutex + /// let c_mutex = Arc::clone(&mutex); + /// let _ = thread::spawn(move || { + /// let mut data = c_mutex.lock().unwrap(); + /// data.insert(10); + /// panic!(); + /// }).join(); + /// + /// let p_err = mutex.lock().unwrap_err(); + /// let data = p_err.into_inner(); + /// println!("recovered {} items", data.len()); + /// ``` + #[stable(feature = "sync_poison", since = "1.2.0")] + pub fn into_inner(self) -> T { + self.guard + } + + /// Reaches into this error indicating that a lock is poisoned, returning a + /// reference to the underlying guard to allow access regardless. + #[stable(feature = "sync_poison", since = "1.2.0")] + pub fn get_ref(&self) -> &T { + &self.guard + } + + /// Reaches into this error indicating that a lock is poisoned, returning a + /// mutable reference to the underlying guard to allow access regardless. + #[stable(feature = "sync_poison", since = "1.2.0")] + pub fn get_mut(&mut self) -> &mut T { + &mut self.guard + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From> for TryLockError { + fn from(err: PoisonError) -> TryLockError { + TryLockError::Poisoned(err) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for TryLockError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f), + TryLockError::WouldBlock => "WouldBlock".fmt(f), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for TryLockError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TryLockError::Poisoned(..) => "poisoned lock: another task failed inside", + TryLockError::WouldBlock => "try_lock failed because the operation would block", + } + .fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for TryLockError { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + match *self { + TryLockError::Poisoned(ref p) => p.description(), + TryLockError::WouldBlock => "try_lock failed because the operation would block", + } + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn Error> { + match *self { + TryLockError::Poisoned(ref p) => Some(p), + _ => None, + } + } +} + +pub fn map_result(result: LockResult, f: F) -> LockResult +where + F: FnOnce(T) -> U, +{ + match result { + Ok(t) => Ok(f(t)), + Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))), + } +} -- cgit v1.2.3