1
Fork 0

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:
Stuart Cook 2024-12-15 20:01:36 +11:00 committed by GitHub
commit 66679081c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 517 additions and 86 deletions

View file

@ -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
///

View file

@ -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!());

View file

@ -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))),
}
}

View file

@ -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
///

View file

@ -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!());