Rollup merge of #133406 - EFanZh:lock-value-accessors, r=Noratrieb
Add value accessor methods to `Mutex` and `RwLock` - ACP: https://github.com/rust-lang/libs-team/issues/485. - Tracking issue: https://github.com/rust-lang/rust/issues/133407. This PR adds `get`, `set` and `replace` methods to the `Mutex` and `RwLock` types for quick access to their contained values. One possible optimization would be to check for poisoning first and return an error immediately, without attempting to acquire the lock. I didn’t implement this because I consider poisoning to be relatively rare, adding this extra check could slow down common use cases.
This commit is contained in:
commit
66679081c7
5 changed files with 517 additions and 86 deletions
|
@ -4,10 +4,10 @@ mod tests;
|
|||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::ManuallyDrop;
|
||||
use crate::mem::{self, ManuallyDrop};
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::{LockResult, TryLockError, TryLockResult, poison};
|
||||
use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison};
|
||||
use crate::sys::sync as sys;
|
||||
|
||||
/// A mutual exclusion primitive useful for protecting shared data
|
||||
|
@ -273,6 +273,100 @@ impl<T> Mutex<T> {
|
|||
pub const fn new(t: T) -> Mutex<T> {
|
||||
Mutex { inner: sys::Mutex::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) }
|
||||
}
|
||||
|
||||
/// Returns the contained value by cloning it.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.get_cloned().unwrap(), 7);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn get_cloned(&self) -> Result<T, PoisonError<()>>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
match self.lock() {
|
||||
Ok(guard) => Ok((*guard).clone()),
|
||||
Err(_) => Err(PoisonError::new(())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the contained value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error containing the provided `value` instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.get_cloned().unwrap(), 7);
|
||||
/// mutex.set(11).unwrap();
|
||||
/// assert_eq!(mutex.get_cloned().unwrap(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn set(&self, value: T) -> Result<(), PoisonError<T>> {
|
||||
if mem::needs_drop::<T>() {
|
||||
// If the contained value has non-trivial destructor, we
|
||||
// call that destructor after the lock being released.
|
||||
self.replace(value).map(drop)
|
||||
} else {
|
||||
match self.lock() {
|
||||
Ok(mut guard) => {
|
||||
*guard = value;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(PoisonError::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the contained value with `value`, and returns the old contained value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error containing the provided `value` instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.replace(11).unwrap(), 7);
|
||||
/// assert_eq!(mutex.get_cloned().unwrap(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn replace(&self, value: T) -> LockResult<T> {
|
||||
match self.lock() {
|
||||
Ok(mut guard) => Ok(mem::replace(&mut *guard, value)),
|
||||
Err(_) => Err(PoisonError::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Mutex<T> {
|
||||
|
@ -290,7 +384,8 @@ impl<T: ?Sized> Mutex<T> {
|
|||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error once the mutex is acquired.
|
||||
/// this call will return an error once the mutex is acquired. The acquired
|
||||
/// mutex guard will be contained in the returned error.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
@ -331,7 +426,8 @@ impl<T: ?Sized> Mutex<T> {
|
|||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return the [`Poisoned`] error if the mutex would
|
||||
/// otherwise be acquired.
|
||||
/// otherwise be acquired. An acquired lock guard will be contained
|
||||
/// in the returned error.
|
||||
///
|
||||
/// If the mutex could not be acquired because it is already locked, then
|
||||
/// this call will return the [`WouldBlock`] error.
|
||||
|
@ -438,7 +534,8 @@ impl<T: ?Sized> Mutex<T> {
|
|||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error instead.
|
||||
/// this call will return an error containing the the underlying data
|
||||
/// instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -465,7 +562,8 @@ impl<T: ?Sized> Mutex<T> {
|
|||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error instead.
|
||||
/// this call will return an error containing a mutable reference to the
|
||||
/// underlying data instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
@ -1,13 +1,34 @@
|
|||
use crate::fmt::Debug;
|
||||
use crate::ops::FnMut;
|
||||
use crate::panic::{self, AssertUnwindSafe};
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering};
|
||||
use crate::sync::mpsc::channel;
|
||||
use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError};
|
||||
use crate::thread;
|
||||
use crate::{hint, mem, thread};
|
||||
|
||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct NonCopy(i32);
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct NonCopyNeedsDrop(i32);
|
||||
|
||||
impl Drop for NonCopyNeedsDrop {
|
||||
fn drop(&mut self) {
|
||||
hint::black_box(());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_needs_drop() {
|
||||
assert!(!mem::needs_drop::<NonCopy>());
|
||||
assert!(mem::needs_drop::<NonCopyNeedsDrop>());
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
struct Cloneable(i32);
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let m = Mutex::new(());
|
||||
|
@ -57,6 +78,21 @@ fn try_lock() {
|
|||
*m.try_lock().unwrap() = ();
|
||||
}
|
||||
|
||||
fn new_poisoned_mutex<T>(value: T) -> Mutex<T> {
|
||||
let mutex = Mutex::new(value);
|
||||
|
||||
let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
let _guard = mutex.lock().unwrap();
|
||||
|
||||
panic!("test panic to poison mutex");
|
||||
}));
|
||||
|
||||
assert!(catch_unwind_result.is_err());
|
||||
assert!(mutex.is_poisoned());
|
||||
|
||||
mutex
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_inner() {
|
||||
let m = Mutex::new(NonCopy(10));
|
||||
|
@ -83,21 +119,31 @@ fn test_into_inner_drop() {
|
|||
|
||||
#[test]
|
||||
fn test_into_inner_poison() {
|
||||
let m = Arc::new(Mutex::new(NonCopy(10)));
|
||||
let m2 = m.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
let _lock = m2.lock().unwrap();
|
||||
panic!("test panic in inner thread to poison mutex");
|
||||
})
|
||||
.join();
|
||||
let m = new_poisoned_mutex(NonCopy(10));
|
||||
|
||||
assert!(m.is_poisoned());
|
||||
match Arc::try_unwrap(m).unwrap().into_inner() {
|
||||
match m.into_inner() {
|
||||
Err(e) => assert_eq!(e.into_inner(), NonCopy(10)),
|
||||
Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cloned() {
|
||||
let m = Mutex::new(Cloneable(10));
|
||||
|
||||
assert_eq!(m.get_cloned().unwrap(), Cloneable(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cloned_poison() {
|
||||
let m = new_poisoned_mutex(Cloneable(10));
|
||||
|
||||
match m.get_cloned() {
|
||||
Err(e) => assert_eq!(e.into_inner(), ()),
|
||||
Ok(x) => panic!("get of poisoned Mutex is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_mut() {
|
||||
let mut m = Mutex::new(NonCopy(10));
|
||||
|
@ -107,21 +153,90 @@ fn test_get_mut() {
|
|||
|
||||
#[test]
|
||||
fn test_get_mut_poison() {
|
||||
let m = Arc::new(Mutex::new(NonCopy(10)));
|
||||
let m2 = m.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
let _lock = m2.lock().unwrap();
|
||||
panic!("test panic in inner thread to poison mutex");
|
||||
})
|
||||
.join();
|
||||
let mut m = new_poisoned_mutex(NonCopy(10));
|
||||
|
||||
assert!(m.is_poisoned());
|
||||
match Arc::try_unwrap(m).unwrap().get_mut() {
|
||||
match m.get_mut() {
|
||||
Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)),
|
||||
Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = Mutex::new(init());
|
||||
|
||||
assert_eq!(*m.lock().unwrap(), init());
|
||||
m.set(value()).unwrap();
|
||||
assert_eq!(*m.lock().unwrap(), value());
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_poison() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = new_poisoned_mutex(init());
|
||||
|
||||
match m.set(value()) {
|
||||
Err(e) => {
|
||||
assert_eq!(e.into_inner(), value());
|
||||
assert_eq!(m.into_inner().unwrap_err().into_inner(), init());
|
||||
}
|
||||
Ok(x) => panic!("set of poisoned Mutex is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = Mutex::new(init());
|
||||
|
||||
assert_eq!(*m.lock().unwrap(), init());
|
||||
assert_eq!(m.replace(value()).unwrap(), init());
|
||||
assert_eq!(*m.lock().unwrap(), value());
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace_poison() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = new_poisoned_mutex(init());
|
||||
|
||||
match m.replace(value()) {
|
||||
Err(e) => {
|
||||
assert_eq!(e.into_inner(), value());
|
||||
assert_eq!(m.into_inner().unwrap_err().into_inner(), init());
|
||||
}
|
||||
Ok(x) => panic!("replace of poisoned Mutex is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_arc_condvar() {
|
||||
let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
|
||||
|
@ -269,7 +384,7 @@ fn test_mapping_mapped_guard() {
|
|||
fn panic_while_mapping_unlocked_poison() {
|
||||
let lock = Mutex::new(());
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.lock().unwrap();
|
||||
let _guard = MutexGuard::map::<(), _>(guard, |_| panic!());
|
||||
});
|
||||
|
@ -282,7 +397,7 @@ fn panic_while_mapping_unlocked_poison() {
|
|||
Err(TryLockError::Poisoned(_)) => {}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.lock().unwrap();
|
||||
let _guard = MutexGuard::try_map::<(), _>(guard, |_| panic!());
|
||||
});
|
||||
|
@ -295,7 +410,7 @@ fn panic_while_mapping_unlocked_poison() {
|
|||
Err(TryLockError::Poisoned(_)) => {}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.lock().unwrap();
|
||||
let guard = MutexGuard::map::<(), _>(guard, |val| val);
|
||||
let _guard = MappedMutexGuard::map::<(), _>(guard, |_| panic!());
|
||||
|
@ -309,7 +424,7 @@ fn panic_while_mapping_unlocked_poison() {
|
|||
Err(TryLockError::Poisoned(_)) => {}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.lock().unwrap();
|
||||
let guard = MutexGuard::map::<(), _>(guard, |val| val);
|
||||
let _guard = MappedMutexGuard::try_map::<(), _>(guard, |_| panic!());
|
||||
|
|
|
@ -87,8 +87,8 @@ pub struct Guard {
|
|||
///
|
||||
/// 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.
|
||||
/// each lock. For a lock in the poisoned state, unless the state is cleared manually,
|
||||
/// all future acquisitions will return this error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -118,7 +118,7 @@ pub struct Guard {
|
|||
/// [`RwLock`]: crate::sync::RwLock
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct PoisonError<T> {
|
||||
guard: T,
|
||||
data: T,
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
_never: !,
|
||||
}
|
||||
|
@ -147,14 +147,15 @@ pub enum TryLockError<T> {
|
|||
/// 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
|
||||
/// poisoned, and the operation result 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.
|
||||
/// an associated value assigned by the lock method, and it can be acquired through the
|
||||
/// [`into_inner`] method. The semantics of the associated value depends on the corresponding
|
||||
/// lock method.
|
||||
///
|
||||
/// [`into_inner`]: PoisonError::into_inner
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
|
||||
pub type LockResult<T> = Result<T, PoisonError<T>>;
|
||||
|
||||
/// A type alias for the result of a nonblocking locking method.
|
||||
///
|
||||
|
@ -195,8 +196,8 @@ impl<T> PoisonError<T> {
|
|||
/// This method may panic if std was built with `panic="abort"`.
|
||||
#[cfg(panic = "unwind")]
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn new(guard: T) -> PoisonError<T> {
|
||||
PoisonError { guard }
|
||||
pub fn new(data: T) -> PoisonError<T> {
|
||||
PoisonError { data }
|
||||
}
|
||||
|
||||
/// Creates a `PoisonError`.
|
||||
|
@ -208,12 +209,12 @@ impl<T> PoisonError<T> {
|
|||
#[cfg(not(panic = "unwind"))]
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
#[track_caller]
|
||||
pub fn new(_guard: T) -> PoisonError<T> {
|
||||
pub fn new(_data: T) -> PoisonError<T> {
|
||||
panic!("PoisonError created in a libstd built with panic=\"abort\"")
|
||||
}
|
||||
|
||||
/// Consumes this error indicating that a lock is poisoned, returning the
|
||||
/// underlying guard to allow access regardless.
|
||||
/// associated data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -238,21 +239,21 @@ impl<T> PoisonError<T> {
|
|||
/// ```
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.guard
|
||||
self.data
|
||||
}
|
||||
|
||||
/// Reaches into this error indicating that a lock is poisoned, returning a
|
||||
/// reference to the underlying guard to allow access regardless.
|
||||
/// reference to the associated data.
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.guard
|
||||
&self.data
|
||||
}
|
||||
|
||||
/// Reaches into this error indicating that a lock is poisoned, returning a
|
||||
/// mutable reference to the underlying guard to allow access regardless.
|
||||
/// mutable reference to the associated data.
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.guard
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,6 +323,6 @@ where
|
|||
match result {
|
||||
Ok(t) => Ok(f(t)),
|
||||
#[cfg(panic = "unwind")]
|
||||
Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))),
|
||||
Err(PoisonError { data }) => Err(PoisonError::new(f(data))),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ mod tests;
|
|||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::{ManuallyDrop, forget};
|
||||
use crate::mem::{self, ManuallyDrop, forget};
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison};
|
||||
|
@ -224,6 +224,103 @@ impl<T> RwLock<T> {
|
|||
pub const fn new(t: T) -> RwLock<T> {
|
||||
RwLock { inner: sys::RwLock::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) }
|
||||
}
|
||||
|
||||
/// Returns the contained value by cloning it.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the `RwLock` is poisoned. An
|
||||
/// `RwLock` is poisoned whenever a writer panics while holding an exclusive
|
||||
/// lock.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::RwLock;
|
||||
///
|
||||
/// let mut lock = RwLock::new(7);
|
||||
///
|
||||
/// assert_eq!(lock.get_cloned().unwrap(), 7);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn get_cloned(&self) -> Result<T, PoisonError<()>>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
match self.read() {
|
||||
Ok(guard) => Ok((*guard).clone()),
|
||||
Err(_) => Err(PoisonError::new(())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the contained value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error containing the provided `value` if
|
||||
/// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer
|
||||
/// panics while holding an exclusive lock.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::RwLock;
|
||||
///
|
||||
/// let mut lock = RwLock::new(7);
|
||||
///
|
||||
/// assert_eq!(lock.get_cloned().unwrap(), 7);
|
||||
/// lock.set(11).unwrap();
|
||||
/// assert_eq!(lock.get_cloned().unwrap(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn set(&self, value: T) -> Result<(), PoisonError<T>> {
|
||||
if mem::needs_drop::<T>() {
|
||||
// If the contained value has non-trivial destructor, we
|
||||
// call that destructor after the lock being released.
|
||||
self.replace(value).map(drop)
|
||||
} else {
|
||||
match self.write() {
|
||||
Ok(mut guard) => {
|
||||
*guard = value;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(PoisonError::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the contained value with `value`, and returns the old contained value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error containing the provided `value` if
|
||||
/// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer
|
||||
/// panics while holding an exclusive lock.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::RwLock;
|
||||
///
|
||||
/// let mut lock = RwLock::new(7);
|
||||
///
|
||||
/// assert_eq!(lock.replace(11).unwrap(), 7);
|
||||
/// assert_eq!(lock.get_cloned().unwrap(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn replace(&self, value: T) -> LockResult<T> {
|
||||
match self.write() {
|
||||
Ok(mut guard) => Ok(mem::replace(&mut *guard, value)),
|
||||
Err(_) => Err(PoisonError::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> RwLock<T> {
|
||||
|
@ -244,7 +341,8 @@ impl<T: ?Sized> RwLock<T> {
|
|||
/// This function will return an error if the `RwLock` is poisoned. An
|
||||
/// `RwLock` is poisoned whenever a writer panics while holding an exclusive
|
||||
/// lock. The failure will occur immediately after the lock has been
|
||||
/// acquired.
|
||||
/// acquired. The acquired lock guard will be contained in the returned
|
||||
/// error.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
@ -292,7 +390,8 @@ impl<T: ?Sized> RwLock<T> {
|
|||
/// This function will return the [`Poisoned`] error if the `RwLock` is
|
||||
/// poisoned. An `RwLock` is poisoned whenever a writer panics while holding
|
||||
/// an exclusive lock. `Poisoned` will only be returned if the lock would
|
||||
/// have otherwise been acquired.
|
||||
/// have otherwise been acquired. An acquired lock guard will be contained
|
||||
/// in the returned error.
|
||||
///
|
||||
/// This function will return the [`WouldBlock`] error if the `RwLock` could
|
||||
/// not be acquired because it was already locked exclusively.
|
||||
|
@ -337,7 +436,8 @@ impl<T: ?Sized> RwLock<T> {
|
|||
///
|
||||
/// This function will return an error if the `RwLock` is poisoned. An
|
||||
/// `RwLock` is poisoned whenever a writer panics while holding an exclusive
|
||||
/// lock. An error will be returned when the lock is acquired.
|
||||
/// lock. An error will be returned when the lock is acquired. The acquired
|
||||
/// lock guard will be contained in the returned error.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
@ -380,7 +480,8 @@ impl<T: ?Sized> RwLock<T> {
|
|||
/// This function will return the [`Poisoned`] error if the `RwLock` is
|
||||
/// poisoned. An `RwLock` is poisoned whenever a writer panics while holding
|
||||
/// an exclusive lock. `Poisoned` will only be returned if the lock would
|
||||
/// have otherwise been acquired.
|
||||
/// have otherwise been acquired. An acquired lock guard will be contained
|
||||
/// in the returned error.
|
||||
///
|
||||
/// This function will return the [`WouldBlock`] error if the `RwLock` could
|
||||
/// not be acquired because it was already locked exclusively.
|
||||
|
@ -481,10 +582,10 @@ impl<T: ?Sized> RwLock<T> {
|
|||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the `RwLock` is poisoned. An
|
||||
/// `RwLock` is poisoned whenever a writer panics while holding an exclusive
|
||||
/// lock. An error will only be returned if the lock would have otherwise
|
||||
/// been acquired.
|
||||
/// This function will return an error containing the underlying data if
|
||||
/// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer
|
||||
/// panics while holding an exclusive lock. An error will only be returned
|
||||
/// if the lock would have otherwise been acquired.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -514,10 +615,11 @@ impl<T: ?Sized> RwLock<T> {
|
|||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the `RwLock` is poisoned. An
|
||||
/// `RwLock` is poisoned whenever a writer panics while holding an exclusive
|
||||
/// lock. An error will only be returned if the lock would have otherwise
|
||||
/// been acquired.
|
||||
/// This function will return an error containing a mutable reference to
|
||||
/// the underlying data if the `RwLock` is poisoned. An `RwLock` is
|
||||
/// poisoned whenever a writer panics while holding an exclusive lock.
|
||||
/// An error will only be returned if the lock would have otherwise been
|
||||
/// acquired.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
@ -1,16 +1,37 @@
|
|||
use rand::Rng;
|
||||
|
||||
use crate::fmt::Debug;
|
||||
use crate::ops::FnMut;
|
||||
use crate::panic::{self, AssertUnwindSafe};
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering};
|
||||
use crate::sync::mpsc::channel;
|
||||
use crate::sync::{
|
||||
Arc, MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard,
|
||||
TryLockError,
|
||||
};
|
||||
use crate::thread;
|
||||
use crate::{hint, mem, thread};
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct NonCopy(i32);
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct NonCopyNeedsDrop(i32);
|
||||
|
||||
impl Drop for NonCopyNeedsDrop {
|
||||
fn drop(&mut self) {
|
||||
hint::black_box(());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_needs_drop() {
|
||||
assert!(!mem::needs_drop::<NonCopy>());
|
||||
assert!(mem::needs_drop::<NonCopyNeedsDrop>());
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
struct Cloneable(i32);
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let l = RwLock::new(());
|
||||
|
@ -255,6 +276,21 @@ fn test_rwlock_try_write() {
|
|||
drop(mapped_read_guard);
|
||||
}
|
||||
|
||||
fn new_poisoned_rwlock<T>(value: T) -> RwLock<T> {
|
||||
let lock = RwLock::new(value);
|
||||
|
||||
let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
let _guard = lock.write().unwrap();
|
||||
|
||||
panic!("test panic to poison RwLock");
|
||||
}));
|
||||
|
||||
assert!(catch_unwind_result.is_err());
|
||||
assert!(lock.is_poisoned());
|
||||
|
||||
lock
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_inner() {
|
||||
let m = RwLock::new(NonCopy(10));
|
||||
|
@ -281,21 +317,31 @@ fn test_into_inner_drop() {
|
|||
|
||||
#[test]
|
||||
fn test_into_inner_poison() {
|
||||
let m = Arc::new(RwLock::new(NonCopy(10)));
|
||||
let m2 = m.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
let _lock = m2.write().unwrap();
|
||||
panic!("test panic in inner thread to poison RwLock");
|
||||
})
|
||||
.join();
|
||||
let m = new_poisoned_rwlock(NonCopy(10));
|
||||
|
||||
assert!(m.is_poisoned());
|
||||
match Arc::try_unwrap(m).unwrap().into_inner() {
|
||||
match m.into_inner() {
|
||||
Err(e) => assert_eq!(e.into_inner(), NonCopy(10)),
|
||||
Ok(x) => panic!("into_inner of poisoned RwLock is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cloned() {
|
||||
let m = RwLock::new(Cloneable(10));
|
||||
|
||||
assert_eq!(m.get_cloned().unwrap(), Cloneable(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cloned_poison() {
|
||||
let m = new_poisoned_rwlock(Cloneable(10));
|
||||
|
||||
match m.get_cloned() {
|
||||
Err(e) => assert_eq!(e.into_inner(), ()),
|
||||
Ok(x) => panic!("get of poisoned RwLock is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_mut() {
|
||||
let mut m = RwLock::new(NonCopy(10));
|
||||
|
@ -305,21 +351,90 @@ fn test_get_mut() {
|
|||
|
||||
#[test]
|
||||
fn test_get_mut_poison() {
|
||||
let m = Arc::new(RwLock::new(NonCopy(10)));
|
||||
let m2 = m.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
let _lock = m2.write().unwrap();
|
||||
panic!("test panic in inner thread to poison RwLock");
|
||||
})
|
||||
.join();
|
||||
let mut m = new_poisoned_rwlock(NonCopy(10));
|
||||
|
||||
assert!(m.is_poisoned());
|
||||
match Arc::try_unwrap(m).unwrap().get_mut() {
|
||||
match m.get_mut() {
|
||||
Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)),
|
||||
Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = RwLock::new(init());
|
||||
|
||||
assert_eq!(*m.read().unwrap(), init());
|
||||
m.set(value()).unwrap();
|
||||
assert_eq!(*m.read().unwrap(), value());
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_poison() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = new_poisoned_rwlock(init());
|
||||
|
||||
match m.set(value()) {
|
||||
Err(e) => {
|
||||
assert_eq!(e.into_inner(), value());
|
||||
assert_eq!(m.into_inner().unwrap_err().into_inner(), init());
|
||||
}
|
||||
Ok(x) => panic!("set of poisoned RwLock is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = RwLock::new(init());
|
||||
|
||||
assert_eq!(*m.read().unwrap(), init());
|
||||
assert_eq!(m.replace(value()).unwrap(), init());
|
||||
assert_eq!(*m.read().unwrap(), value());
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace_poison() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = new_poisoned_rwlock(init());
|
||||
|
||||
match m.replace(value()) {
|
||||
Err(e) => {
|
||||
assert_eq!(e.into_inner(), value());
|
||||
assert_eq!(m.into_inner().unwrap_err().into_inner(), init());
|
||||
}
|
||||
Ok(x) => panic!("replace of poisoned RwLock is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_guard_covariance() {
|
||||
fn do_stuff<'a>(_: RwLockReadGuard<'_, &'a i32>, _: &'a i32) {}
|
||||
|
@ -370,7 +485,7 @@ fn test_mapping_mapped_guard() {
|
|||
fn panic_while_mapping_read_unlocked_no_poison() {
|
||||
let lock = RwLock::new(());
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.read().unwrap();
|
||||
let _guard = RwLockReadGuard::map::<(), _>(guard, |_| panic!());
|
||||
});
|
||||
|
@ -385,7 +500,7 @@ fn panic_while_mapping_read_unlocked_no_poison() {
|
|||
}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.read().unwrap();
|
||||
let _guard = RwLockReadGuard::try_map::<(), _>(guard, |_| panic!());
|
||||
});
|
||||
|
@ -400,7 +515,7 @@ fn panic_while_mapping_read_unlocked_no_poison() {
|
|||
}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.read().unwrap();
|
||||
let guard = RwLockReadGuard::map::<(), _>(guard, |val| val);
|
||||
let _guard = MappedRwLockReadGuard::map::<(), _>(guard, |_| panic!());
|
||||
|
@ -416,7 +531,7 @@ fn panic_while_mapping_read_unlocked_no_poison() {
|
|||
}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.read().unwrap();
|
||||
let guard = RwLockReadGuard::map::<(), _>(guard, |val| val);
|
||||
let _guard = MappedRwLockReadGuard::try_map::<(), _>(guard, |_| panic!());
|
||||
|
@ -439,7 +554,7 @@ fn panic_while_mapping_read_unlocked_no_poison() {
|
|||
fn panic_while_mapping_write_unlocked_poison() {
|
||||
let lock = RwLock::new(());
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.write().unwrap();
|
||||
let _guard = RwLockWriteGuard::map::<(), _>(guard, |_| panic!());
|
||||
});
|
||||
|
@ -452,7 +567,7 @@ fn panic_while_mapping_write_unlocked_poison() {
|
|||
Err(TryLockError::Poisoned(_)) => {}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.write().unwrap();
|
||||
let _guard = RwLockWriteGuard::try_map::<(), _>(guard, |_| panic!());
|
||||
});
|
||||
|
@ -467,7 +582,7 @@ fn panic_while_mapping_write_unlocked_poison() {
|
|||
Err(TryLockError::Poisoned(_)) => {}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.write().unwrap();
|
||||
let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val);
|
||||
let _guard = MappedRwLockWriteGuard::map::<(), _>(guard, |_| panic!());
|
||||
|
@ -483,7 +598,7 @@ fn panic_while_mapping_write_unlocked_poison() {
|
|||
Err(TryLockError::Poisoned(_)) => {}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.write().unwrap();
|
||||
let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val);
|
||||
let _guard = MappedRwLockWriteGuard::try_map::<(), _>(guard, |_| panic!());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue