From 9cabbd0afcfd8aa5724501bea96fb98688913d0b Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Wed, 21 Apr 2021 10:05:11 +0200 Subject: Move `sys_common::poison` to `sync::poison` --- library/std/src/sync/condvar.rs | 3 +- library/std/src/sync/mod.rs | 5 +- library/std/src/sync/mutex.rs | 2 +- library/std/src/sync/poison.rs | 259 +++++++++++++++++++++++++++++++++++ library/std/src/sync/rwlock.rs | 2 +- library/std/src/sys_common/mod.rs | 1 - library/std/src/sys_common/poison.rs | 257 ---------------------------------- 7 files changed, 265 insertions(+), 264 deletions(-) create mode 100644 library/std/src/sync/poison.rs delete mode 100644 library/std/src/sys_common/poison.rs (limited to 'library/std/src') diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs index 60be63c9543..2f0b32c90d0 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/condvar.rs @@ -2,9 +2,8 @@ mod tests; use crate::fmt; -use crate::sync::{mutex, MutexGuard, PoisonError}; +use crate::sync::{mutex, poison, LockResult, MutexGuard, PoisonError}; use crate::sys_common::condvar as sys; -use crate::sys_common::poison::{self, LockResult}; use crate::time::{Duration, Instant}; /// A type indicating whether a timed wait on a condition variable returned diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index b6699910b07..ee35598bab5 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -166,9 +166,9 @@ pub use self::mutex::{Mutex, MutexGuard}; #[allow(deprecated)] pub use self::once::{Once, OnceState, ONCE_INIT}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +pub use self::poison::{LockResult, PoisonError, TryLockError, TryLockResult}; #[stable(feature = "rust1", since = "1.0.0")] -pub use crate::sys_common::poison::{LockResult, PoisonError, TryLockError, TryLockResult}; +pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; pub mod mpsc; @@ -176,4 +176,5 @@ mod barrier; mod condvar; mod mutex; mod once; +mod poison; mod rwlock; diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index 98c34282e0c..2615bea6592 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -6,8 +6,8 @@ use crate::fmt; use crate::mem; use crate::ops::{Deref, DerefMut}; use crate::ptr; +use crate::sync::{poison, LockResult, TryLockError, TryLockResult}; use crate::sys_common::mutex as sys; -use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; /// A mutual exclusion primitive useful for protecting shared data /// diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs new file mode 100644 index 00000000000..05e1833c3e5 --- /dev/null +++ b/library/std/src/sync/poison.rs @@ -0,0 +1,259 @@ +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 { + pub const fn new() -> Flag { + Flag { failed: AtomicBool::new(false) } + } + + #[inline] + pub fn borrow(&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) + } +} + +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 may 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))), + } +} diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index 351804ec979..b01bcec1361 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -6,7 +6,7 @@ use crate::fmt; use crate::mem; use crate::ops::{Deref, DerefMut}; use crate::ptr; -use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; +use crate::sync::{poison, LockResult, TryLockError, TryLockResult}; use crate::sys_common::rwlock as sys; /// A reader-writer lock diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 23a3a0e907d..4979bc0b5af 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -59,7 +59,6 @@ pub mod mutex; // when generating documentation. #[cfg(any(doc, not(windows)))] pub mod os_str_bytes; -pub mod poison; pub mod process; pub mod remutex; pub mod rwlock; diff --git a/library/std/src/sys_common/poison.rs b/library/std/src/sys_common/poison.rs deleted file mode 100644 index 1f71187f1b4..00000000000 --- a/library/std/src/sys_common/poison.rs +++ /dev/null @@ -1,257 +0,0 @@ -use crate::error::Error; -use crate::fmt; -use crate::sync::atomic::{AtomicBool, Ordering}; -use crate::thread; - -#[allow(unused_imports)] // for intra-doc links -use crate::sync::{Mutex, RwLock}; - -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 { - pub const fn new() -> Flag { - Flag { failed: AtomicBool::new(false) } - } - - #[inline] - pub fn borrow(&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) - } -} - -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); -/// } -/// }; -/// ``` -#[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`]: Mutex::try_lock -/// [`try_read`]: RwLock::try_read -/// [`try_write`]: RwLock::try_write -#[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 may 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`] or [`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 1.4.1-3-g733a5