Specify behavior if the closure passed to *Guard::*map panics.
This commit is contained in:
@@ -612,10 +612,14 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> {
|
|||||||
F: FnOnce(&mut T) -> &mut U,
|
F: FnOnce(&mut T) -> &mut U,
|
||||||
U: ?Sized,
|
U: ?Sized,
|
||||||
{
|
{
|
||||||
let mut orig = ManuallyDrop::new(orig);
|
// SAFETY: the conditions of `MutedGuard::new` were satisfied when the original guard
|
||||||
let value = NonNull::from(f(&mut *orig));
|
// was created, and have been upheld throughout `map` and/or `try_map`.
|
||||||
|
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||||
|
// passed to it. If the closure panics, the guard will be dropped.
|
||||||
|
let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() }));
|
||||||
|
let orig = ManuallyDrop::new(orig);
|
||||||
MappedMutexGuard {
|
MappedMutexGuard {
|
||||||
data: value,
|
data,
|
||||||
inner: &orig.lock.inner,
|
inner: &orig.lock.inner,
|
||||||
poison_flag: &orig.lock.poison,
|
poison_flag: &orig.lock.poison,
|
||||||
poison: orig.poison.clone(),
|
poison: orig.poison.clone(),
|
||||||
@@ -639,16 +643,23 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> {
|
|||||||
F: FnOnce(&mut T) -> Option<&mut U>,
|
F: FnOnce(&mut T) -> Option<&mut U>,
|
||||||
U: ?Sized,
|
U: ?Sized,
|
||||||
{
|
{
|
||||||
let mut orig = ManuallyDrop::new(orig);
|
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||||
match f(&mut *orig).map(NonNull::from) {
|
// was created, and have been upheld throughout `map` and/or `try_map`.
|
||||||
Some(value) => Ok(MappedMutexGuard {
|
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||||
data: value,
|
// passed to it. If the closure panics, the guard will be dropped.
|
||||||
inner: &orig.lock.inner,
|
match f(unsafe { &mut *orig.lock.data.get() }) {
|
||||||
poison_flag: &orig.lock.poison,
|
Some(data) => {
|
||||||
poison: orig.poison.clone(),
|
let data = NonNull::from(data);
|
||||||
_variance: PhantomData,
|
let orig = ManuallyDrop::new(orig);
|
||||||
}),
|
Ok(MappedMutexGuard {
|
||||||
None => Err(ManuallyDrop::into_inner(orig)),
|
data,
|
||||||
|
inner: &orig.lock.inner,
|
||||||
|
poison_flag: &orig.lock.poison,
|
||||||
|
poison: orig.poison.clone(),
|
||||||
|
_variance: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
None => Err(orig),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -704,15 +715,19 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
|
|||||||
/// `MappedMutexGuard::map(...)`. A method would interfere with methods of the
|
/// `MappedMutexGuard::map(...)`. A method would interfere with methods of the
|
||||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||||
pub fn map<U, F>(orig: Self, f: F) -> MappedMutexGuard<'a, U>
|
pub fn map<U, F>(mut orig: Self, f: F) -> MappedMutexGuard<'a, U>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut T) -> &mut U,
|
F: FnOnce(&mut T) -> &mut U,
|
||||||
U: ?Sized,
|
U: ?Sized,
|
||||||
{
|
{
|
||||||
let mut orig = ManuallyDrop::new(orig);
|
// SAFETY: the conditions of `MutedGuard::new` were satisfied when the original guard
|
||||||
let value = NonNull::from(f(&mut *orig));
|
// was created, and have been upheld throughout `map` and/or `try_map`.
|
||||||
|
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||||
|
// passed to it. If the closure panics, the guard will be dropped.
|
||||||
|
let data = NonNull::from(f(unsafe { orig.data.as_mut() }));
|
||||||
|
let orig = ManuallyDrop::new(orig);
|
||||||
MappedMutexGuard {
|
MappedMutexGuard {
|
||||||
data: value,
|
data,
|
||||||
inner: orig.inner,
|
inner: orig.inner,
|
||||||
poison_flag: orig.poison_flag,
|
poison_flag: orig.poison_flag,
|
||||||
poison: orig.poison.clone(),
|
poison: orig.poison.clone(),
|
||||||
@@ -731,21 +746,28 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
|
|||||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||||
#[doc(alias = "filter_map")]
|
#[doc(alias = "filter_map")]
|
||||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||||
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
|
pub fn try_map<U, F>(mut orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut T) -> Option<&mut U>,
|
F: FnOnce(&mut T) -> Option<&mut U>,
|
||||||
U: ?Sized,
|
U: ?Sized,
|
||||||
{
|
{
|
||||||
let mut orig = ManuallyDrop::new(orig);
|
// SAFETY: the conditions of `MutedGuard::new` were satisfied when the original guard
|
||||||
match f(&mut *orig).map(NonNull::from) {
|
// was created, and have been upheld throughout `map` and/or `try_map`.
|
||||||
Some(value) => Ok(MappedMutexGuard {
|
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||||
data: value,
|
// passed to it. If the closure panics, the guard will be dropped.
|
||||||
inner: orig.inner,
|
match f(unsafe { orig.data.as_mut() }) {
|
||||||
poison_flag: orig.poison_flag,
|
Some(data) => {
|
||||||
poison: orig.poison.clone(),
|
let data = NonNull::from(data);
|
||||||
_variance: PhantomData,
|
let orig = ManuallyDrop::new(orig);
|
||||||
}),
|
Ok(MappedMutexGuard {
|
||||||
None => Err(ManuallyDrop::into_inner(orig)),
|
data,
|
||||||
|
inner: orig.inner,
|
||||||
|
poison_flag: orig.poison_flag,
|
||||||
|
poison: orig.poison.clone(),
|
||||||
|
_variance: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
None => Err(orig),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::sync::atomic::{AtomicUsize, Ordering};
|
use crate::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use crate::sync::mpsc::channel;
|
use crate::sync::mpsc::channel;
|
||||||
use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard};
|
use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError};
|
||||||
use crate::thread;
|
use crate::thread;
|
||||||
|
|
||||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||||
@@ -264,3 +264,64 @@ fn test_mapping_mapped_guard() {
|
|||||||
drop(guard);
|
drop(guard);
|
||||||
assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]);
|
assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn panic_while_mapping_unlocked_poison() {
|
||||||
|
let lock = Mutex::new(());
|
||||||
|
|
||||||
|
let _ = crate::panic::catch_unwind(|| {
|
||||||
|
let guard = lock.lock().unwrap();
|
||||||
|
let _guard = MutexGuard::map::<(), _>(guard, |_| panic!());
|
||||||
|
});
|
||||||
|
|
||||||
|
match lock.try_lock() {
|
||||||
|
Ok(_) => panic!("panicking in a MutexGuard::map closure should poison the Mutex"),
|
||||||
|
Err(TryLockError::WouldBlock) => {
|
||||||
|
panic!("panicking in a MutexGuard::map closure should unlock the mutex")
|
||||||
|
}
|
||||||
|
Err(TryLockError::Poisoned(_)) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = crate::panic::catch_unwind(|| {
|
||||||
|
let guard = lock.lock().unwrap();
|
||||||
|
let _guard = MutexGuard::try_map::<(), _>(guard, |_| panic!());
|
||||||
|
});
|
||||||
|
|
||||||
|
match lock.try_lock() {
|
||||||
|
Ok(_) => panic!("panicking in a MutexGuard::try_map closure should poison the Mutex"),
|
||||||
|
Err(TryLockError::WouldBlock) => {
|
||||||
|
panic!("panicking in a MutexGuard::try_map closure should unlock the mutex")
|
||||||
|
}
|
||||||
|
Err(TryLockError::Poisoned(_)) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = crate::panic::catch_unwind(|| {
|
||||||
|
let guard = lock.lock().unwrap();
|
||||||
|
let guard = MutexGuard::map::<(), _>(guard, |val| val);
|
||||||
|
let _guard = MappedMutexGuard::map::<(), _>(guard, |_| panic!());
|
||||||
|
});
|
||||||
|
|
||||||
|
match lock.try_lock() {
|
||||||
|
Ok(_) => panic!("panicking in a MappedMutexGuard::map closure should poison the Mutex"),
|
||||||
|
Err(TryLockError::WouldBlock) => {
|
||||||
|
panic!("panicking in a MappedMutexGuard::map closure should unlock the mutex")
|
||||||
|
}
|
||||||
|
Err(TryLockError::Poisoned(_)) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = crate::panic::catch_unwind(|| {
|
||||||
|
let guard = lock.lock().unwrap();
|
||||||
|
let guard = MutexGuard::map::<(), _>(guard, |val| val);
|
||||||
|
let _guard = MappedMutexGuard::try_map::<(), _>(guard, |_| panic!());
|
||||||
|
});
|
||||||
|
|
||||||
|
match lock.try_lock() {
|
||||||
|
Ok(_) => panic!("panicking in a MappedMutexGuard::try_map closure should poison the Mutex"),
|
||||||
|
Err(TryLockError::WouldBlock) => {
|
||||||
|
panic!("panicking in a MappedMutexGuard::try_map closure should unlock the mutex")
|
||||||
|
}
|
||||||
|
Err(TryLockError::Poisoned(_)) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(lock);
|
||||||
|
}
|
||||||
|
|||||||
@@ -766,15 +766,23 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> {
|
|||||||
/// `RwLockReadGuard::map(...)`. A method would interfere with methods of
|
/// `RwLockReadGuard::map(...)`. A method would interfere with methods of
|
||||||
/// the same name on the contents of the `RwLockReadGuard` used through
|
/// the same name on the contents of the `RwLockReadGuard` used through
|
||||||
/// `Deref`.
|
/// `Deref`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be poisoned.
|
||||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||||
pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockReadGuard<'a, U>
|
pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockReadGuard<'a, U>
|
||||||
where
|
where
|
||||||
F: FnOnce(&T) -> &U,
|
F: FnOnce(&T) -> &U,
|
||||||
U: ?Sized,
|
U: ?Sized,
|
||||||
{
|
{
|
||||||
|
// SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard
|
||||||
|
// was created, and have been upheld throughout `map` and/or `try_map`.
|
||||||
|
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||||
|
// passed to it. If the closure panics, the guard will be dropped.
|
||||||
|
let data = NonNull::from(f(unsafe { orig.data.as_ref() }));
|
||||||
let orig = ManuallyDrop::new(orig);
|
let orig = ManuallyDrop::new(orig);
|
||||||
let value = NonNull::from(f(&*orig));
|
MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }
|
||||||
MappedRwLockReadGuard { data: value, inner_lock: &orig.inner_lock }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. The
|
/// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. The
|
||||||
@@ -787,6 +795,10 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> {
|
|||||||
/// `RwLockReadGuard::try_map(...)`. A method would interfere with methods
|
/// `RwLockReadGuard::try_map(...)`. A method would interfere with methods
|
||||||
/// of the same name on the contents of the `RwLockReadGuard` used through
|
/// of the same name on the contents of the `RwLockReadGuard` used through
|
||||||
/// `Deref`.
|
/// `Deref`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be poisoned.
|
||||||
#[doc(alias = "filter_map")]
|
#[doc(alias = "filter_map")]
|
||||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||||
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'a, U>, Self>
|
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'a, U>, Self>
|
||||||
@@ -794,10 +806,17 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> {
|
|||||||
F: FnOnce(&T) -> Option<&U>,
|
F: FnOnce(&T) -> Option<&U>,
|
||||||
U: ?Sized,
|
U: ?Sized,
|
||||||
{
|
{
|
||||||
let orig = ManuallyDrop::new(orig);
|
// SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard
|
||||||
match f(&*orig).map(NonNull::from) {
|
// was created, and have been upheld throughout `map` and/or `try_map`.
|
||||||
Some(value) => Ok(MappedRwLockReadGuard { data: value, inner_lock: &orig.inner_lock }),
|
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||||
None => Err(ManuallyDrop::into_inner(orig)),
|
// passed to it. If the closure panics, the guard will be dropped.
|
||||||
|
match f(unsafe { orig.data.as_ref() }) {
|
||||||
|
Some(data) => {
|
||||||
|
let data = NonNull::from(data);
|
||||||
|
let orig = ManuallyDrop::new(orig);
|
||||||
|
Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock })
|
||||||
|
}
|
||||||
|
None => Err(orig),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -812,15 +831,23 @@ impl<'a, T: ?Sized> MappedRwLockReadGuard<'a, T> {
|
|||||||
/// `MappedRwLockReadGuard::map(...)`. A method would interfere with
|
/// `MappedRwLockReadGuard::map(...)`. A method would interfere with
|
||||||
/// methods of the same name on the contents of the `MappedRwLockReadGuard`
|
/// methods of the same name on the contents of the `MappedRwLockReadGuard`
|
||||||
/// used through `Deref`.
|
/// used through `Deref`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be poisoned.
|
||||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||||
pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockReadGuard<'a, U>
|
pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockReadGuard<'a, U>
|
||||||
where
|
where
|
||||||
F: FnOnce(&T) -> &U,
|
F: FnOnce(&T) -> &U,
|
||||||
U: ?Sized,
|
U: ?Sized,
|
||||||
{
|
{
|
||||||
|
// SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard
|
||||||
|
// was created, and have been upheld throughout `map` and/or `try_map`.
|
||||||
|
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||||
|
// passed to it. If the closure panics, the guard will be dropped.
|
||||||
|
let data = NonNull::from(f(unsafe { orig.data.as_ref() }));
|
||||||
let orig = ManuallyDrop::new(orig);
|
let orig = ManuallyDrop::new(orig);
|
||||||
let value = NonNull::from(f(&*orig));
|
MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }
|
||||||
MappedRwLockReadGuard { data: value, inner_lock: &orig.inner_lock }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data.
|
/// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data.
|
||||||
@@ -833,6 +860,10 @@ impl<'a, T: ?Sized> MappedRwLockReadGuard<'a, T> {
|
|||||||
/// `MappedRwLockReadGuard::try_map(...)`. A method would interfere with
|
/// `MappedRwLockReadGuard::try_map(...)`. A method would interfere with
|
||||||
/// methods of the same name on the contents of the `MappedRwLockReadGuard`
|
/// methods of the same name on the contents of the `MappedRwLockReadGuard`
|
||||||
/// used through `Deref`.
|
/// used through `Deref`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be poisoned.
|
||||||
#[doc(alias = "filter_map")]
|
#[doc(alias = "filter_map")]
|
||||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||||
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'a, U>, Self>
|
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'a, U>, Self>
|
||||||
@@ -840,10 +871,17 @@ impl<'a, T: ?Sized> MappedRwLockReadGuard<'a, T> {
|
|||||||
F: FnOnce(&T) -> Option<&U>,
|
F: FnOnce(&T) -> Option<&U>,
|
||||||
U: ?Sized,
|
U: ?Sized,
|
||||||
{
|
{
|
||||||
let orig = ManuallyDrop::new(orig);
|
// SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard
|
||||||
match f(&*orig).map(NonNull::from) {
|
// was created, and have been upheld throughout `map` and/or `try_map`.
|
||||||
Some(value) => Ok(MappedRwLockReadGuard { data: value, inner_lock: &orig.inner_lock }),
|
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||||
None => Err(ManuallyDrop::into_inner(orig)),
|
// passed to it. If the closure panics, the guard will be dropped.
|
||||||
|
match f(unsafe { orig.data.as_ref() }) {
|
||||||
|
Some(data) => {
|
||||||
|
let data = NonNull::from(data);
|
||||||
|
let orig = ManuallyDrop::new(orig);
|
||||||
|
Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock })
|
||||||
|
}
|
||||||
|
None => Err(orig),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -858,16 +896,24 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
|
|||||||
/// `RwLockWriteGuard::map(...)`. A method would interfere with methods of
|
/// `RwLockWriteGuard::map(...)`. A method would interfere with methods of
|
||||||
/// the same name on the contents of the `RwLockWriteGuard` used through
|
/// the same name on the contents of the `RwLockWriteGuard` used through
|
||||||
/// `Deref`.
|
/// `Deref`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned.
|
||||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||||
pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockWriteGuard<'a, U>
|
pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockWriteGuard<'a, U>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut T) -> &mut U,
|
F: FnOnce(&mut T) -> &mut U,
|
||||||
U: ?Sized,
|
U: ?Sized,
|
||||||
{
|
{
|
||||||
let mut orig = ManuallyDrop::new(orig);
|
// SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard
|
||||||
let value = NonNull::from(f(&mut *orig));
|
// was created, and have been upheld throughout `map` and/or `try_map`.
|
||||||
|
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||||
|
// passed to it. If the closure panics, the guard will be dropped.
|
||||||
|
let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() }));
|
||||||
|
let orig = ManuallyDrop::new(orig);
|
||||||
MappedRwLockWriteGuard {
|
MappedRwLockWriteGuard {
|
||||||
data: value,
|
data,
|
||||||
inner_lock: &orig.lock.inner,
|
inner_lock: &orig.lock.inner,
|
||||||
poison_flag: &orig.lock.poison,
|
poison_flag: &orig.lock.poison,
|
||||||
poison: orig.poison.clone(),
|
poison: orig.poison.clone(),
|
||||||
@@ -885,6 +931,10 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
|
|||||||
/// `RwLockWriteGuard::try_map(...)`. A method would interfere with methods
|
/// `RwLockWriteGuard::try_map(...)`. A method would interfere with methods
|
||||||
/// of the same name on the contents of the `RwLockWriteGuard` used through
|
/// of the same name on the contents of the `RwLockWriteGuard` used through
|
||||||
/// `Deref`.
|
/// `Deref`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned.
|
||||||
#[doc(alias = "filter_map")]
|
#[doc(alias = "filter_map")]
|
||||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||||
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockWriteGuard<'a, U>, Self>
|
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockWriteGuard<'a, U>, Self>
|
||||||
@@ -892,16 +942,23 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
|
|||||||
F: FnOnce(&mut T) -> Option<&mut U>,
|
F: FnOnce(&mut T) -> Option<&mut U>,
|
||||||
U: ?Sized,
|
U: ?Sized,
|
||||||
{
|
{
|
||||||
let mut orig = ManuallyDrop::new(orig);
|
// SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard
|
||||||
match f(&mut *orig).map(NonNull::from) {
|
// was created, and have been upheld throughout `map` and/or `try_map`.
|
||||||
Some(value) => Ok(MappedRwLockWriteGuard {
|
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||||
data: value,
|
// passed to it. If the closure panics, the guard will be dropped.
|
||||||
inner_lock: &orig.lock.inner,
|
match f(unsafe { &mut *orig.lock.data.get() }) {
|
||||||
poison_flag: &orig.lock.poison,
|
Some(data) => {
|
||||||
poison: orig.poison.clone(),
|
let data = NonNull::from(data);
|
||||||
_variance: PhantomData,
|
let orig = ManuallyDrop::new(orig);
|
||||||
}),
|
Ok(MappedRwLockWriteGuard {
|
||||||
None => Err(ManuallyDrop::into_inner(orig)),
|
data,
|
||||||
|
inner_lock: &orig.lock.inner,
|
||||||
|
poison_flag: &orig.lock.poison,
|
||||||
|
poison: orig.poison.clone(),
|
||||||
|
_variance: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
None => Err(orig),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -916,16 +973,24 @@ impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> {
|
|||||||
/// `MappedRwLockWriteGuard::map(...)`. A method would interfere with
|
/// `MappedRwLockWriteGuard::map(...)`. A method would interfere with
|
||||||
/// methods of the same name on the contents of the `MappedRwLockWriteGuard`
|
/// methods of the same name on the contents of the `MappedRwLockWriteGuard`
|
||||||
/// used through `Deref`.
|
/// used through `Deref`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned.
|
||||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||||
pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockWriteGuard<'a, U>
|
pub fn map<U, F>(mut orig: Self, f: F) -> MappedRwLockWriteGuard<'a, U>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut T) -> &mut U,
|
F: FnOnce(&mut T) -> &mut U,
|
||||||
U: ?Sized,
|
U: ?Sized,
|
||||||
{
|
{
|
||||||
let mut orig = ManuallyDrop::new(orig);
|
// SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard
|
||||||
let value = NonNull::from(f(&mut *orig));
|
// was created, and have been upheld throughout `map` and/or `try_map`.
|
||||||
|
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||||
|
// passed to it. If the closure panics, the guard will be dropped.
|
||||||
|
let data = NonNull::from(f(unsafe { orig.data.as_mut() }));
|
||||||
|
let orig = ManuallyDrop::new(orig);
|
||||||
MappedRwLockWriteGuard {
|
MappedRwLockWriteGuard {
|
||||||
data: value,
|
data,
|
||||||
inner_lock: orig.inner_lock,
|
inner_lock: orig.inner_lock,
|
||||||
poison_flag: orig.poison_flag,
|
poison_flag: orig.poison_flag,
|
||||||
poison: orig.poison.clone(),
|
poison: orig.poison.clone(),
|
||||||
@@ -943,23 +1008,34 @@ impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> {
|
|||||||
/// `MappedRwLockWriteGuard::try_map(...)`. A method would interfere with
|
/// `MappedRwLockWriteGuard::try_map(...)`. A method would interfere with
|
||||||
/// methods of the same name on the contents of the `MappedRwLockWriteGuard`
|
/// methods of the same name on the contents of the `MappedRwLockWriteGuard`
|
||||||
/// used through `Deref`.
|
/// used through `Deref`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned.
|
||||||
#[doc(alias = "filter_map")]
|
#[doc(alias = "filter_map")]
|
||||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||||
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockWriteGuard<'a, U>, Self>
|
pub fn try_map<U, F>(mut orig: Self, f: F) -> Result<MappedRwLockWriteGuard<'a, U>, Self>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut T) -> Option<&mut U>,
|
F: FnOnce(&mut T) -> Option<&mut U>,
|
||||||
U: ?Sized,
|
U: ?Sized,
|
||||||
{
|
{
|
||||||
let mut orig = ManuallyDrop::new(orig);
|
// SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard
|
||||||
match f(&mut *orig).map(NonNull::from) {
|
// was created, and have been upheld throughout `map` and/or `try_map`.
|
||||||
Some(value) => Ok(MappedRwLockWriteGuard {
|
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||||
data: value,
|
// passed to it. If the closure panics, the guard will be dropped.
|
||||||
inner_lock: orig.inner_lock,
|
match f(unsafe { orig.data.as_mut() }) {
|
||||||
poison_flag: orig.poison_flag,
|
Some(data) => {
|
||||||
poison: orig.poison.clone(),
|
let data = NonNull::from(data);
|
||||||
_variance: PhantomData,
|
let orig = ManuallyDrop::new(orig);
|
||||||
}),
|
Ok(MappedRwLockWriteGuard {
|
||||||
None => Err(ManuallyDrop::into_inner(orig)),
|
data,
|
||||||
|
inner_lock: orig.inner_lock,
|
||||||
|
poison_flag: orig.poison_flag,
|
||||||
|
poison: orig.poison.clone(),
|
||||||
|
_variance: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
None => Err(orig),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -360,3 +360,139 @@ fn test_mapping_mapped_guard() {
|
|||||||
drop(guard);
|
drop(guard);
|
||||||
assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]);
|
assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn panic_while_mapping_read_unlocked_no_poison() {
|
||||||
|
let lock = RwLock::new(());
|
||||||
|
|
||||||
|
let _ = crate::panic::catch_unwind(|| {
|
||||||
|
let guard = lock.read().unwrap();
|
||||||
|
let _guard = RwLockReadGuard::map::<(), _>(guard, |_| panic!());
|
||||||
|
});
|
||||||
|
|
||||||
|
match lock.try_write() {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(TryLockError::WouldBlock) => {
|
||||||
|
panic!("panicking in a RwLockReadGuard::map closure should release the read lock")
|
||||||
|
}
|
||||||
|
Err(TryLockError::Poisoned(_)) => {
|
||||||
|
panic!("panicking in a RwLockReadGuard::map closure should not poison the RwLock")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = crate::panic::catch_unwind(|| {
|
||||||
|
let guard = lock.read().unwrap();
|
||||||
|
let _guard = RwLockReadGuard::try_map::<(), _>(guard, |_| panic!());
|
||||||
|
});
|
||||||
|
|
||||||
|
match lock.try_write() {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(TryLockError::WouldBlock) => {
|
||||||
|
panic!("panicking in a RwLockReadGuard::try_map closure should release the read lock")
|
||||||
|
}
|
||||||
|
Err(TryLockError::Poisoned(_)) => {
|
||||||
|
panic!("panicking in a RwLockReadGuard::try_map closure should not poison the RwLock")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = crate::panic::catch_unwind(|| {
|
||||||
|
let guard = lock.read().unwrap();
|
||||||
|
let guard = RwLockReadGuard::map::<(), _>(guard, |val| val);
|
||||||
|
let _guard = MappedRwLockReadGuard::map::<(), _>(guard, |_| panic!());
|
||||||
|
});
|
||||||
|
|
||||||
|
match lock.try_write() {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(TryLockError::WouldBlock) => {
|
||||||
|
panic!("panicking in a MappedRwLockReadGuard::map closure should release the read lock")
|
||||||
|
}
|
||||||
|
Err(TryLockError::Poisoned(_)) => {
|
||||||
|
panic!("panicking in a MappedRwLockReadGuard::map closure should not poison the RwLock")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = crate::panic::catch_unwind(|| {
|
||||||
|
let guard = lock.read().unwrap();
|
||||||
|
let guard = RwLockReadGuard::map::<(), _>(guard, |val| val);
|
||||||
|
let _guard = MappedRwLockReadGuard::try_map::<(), _>(guard, |_| panic!());
|
||||||
|
});
|
||||||
|
|
||||||
|
match lock.try_write() {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(TryLockError::WouldBlock) => panic!(
|
||||||
|
"panicking in a MappedRwLockReadGuard::try_map closure should release the read lock"
|
||||||
|
),
|
||||||
|
Err(TryLockError::Poisoned(_)) => panic!(
|
||||||
|
"panicking in a MappedRwLockReadGuard::try_map closure should not poison the RwLock"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn panic_while_mapping_write_unlocked_poison() {
|
||||||
|
let lock = RwLock::new(());
|
||||||
|
|
||||||
|
let _ = crate::panic::catch_unwind(|| {
|
||||||
|
let guard = lock.write().unwrap();
|
||||||
|
let _guard = RwLockWriteGuard::map::<(), _>(guard, |_| panic!());
|
||||||
|
});
|
||||||
|
|
||||||
|
match lock.try_write() {
|
||||||
|
Ok(_) => panic!("panicking in a RwLockWriteGuard::map closure should poison the RwLock"),
|
||||||
|
Err(TryLockError::WouldBlock) => {
|
||||||
|
panic!("panicking in a RwLockWriteGuard::map closure should release the write lock")
|
||||||
|
}
|
||||||
|
Err(TryLockError::Poisoned(_)) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = crate::panic::catch_unwind(|| {
|
||||||
|
let guard = lock.write().unwrap();
|
||||||
|
let _guard = RwLockWriteGuard::try_map::<(), _>(guard, |_| panic!());
|
||||||
|
});
|
||||||
|
|
||||||
|
match lock.try_write() {
|
||||||
|
Ok(_) => {
|
||||||
|
panic!("panicking in a RwLockWriteGuard::try_map closure should poison the RwLock")
|
||||||
|
}
|
||||||
|
Err(TryLockError::WouldBlock) => {
|
||||||
|
panic!("panicking in a RwLockWriteGuard::try_map closure should release the write lock")
|
||||||
|
}
|
||||||
|
Err(TryLockError::Poisoned(_)) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = crate::panic::catch_unwind(|| {
|
||||||
|
let guard = lock.write().unwrap();
|
||||||
|
let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val);
|
||||||
|
let _guard = MappedRwLockWriteGuard::map::<(), _>(guard, |_| panic!());
|
||||||
|
});
|
||||||
|
|
||||||
|
match lock.try_write() {
|
||||||
|
Ok(_) => {
|
||||||
|
panic!("panicking in a MappedRwLockWriteGuard::map closure should poison the RwLock")
|
||||||
|
}
|
||||||
|
Err(TryLockError::WouldBlock) => panic!(
|
||||||
|
"panicking in a MappedRwLockWriteGuard::map closure should release the write lock"
|
||||||
|
),
|
||||||
|
Err(TryLockError::Poisoned(_)) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = crate::panic::catch_unwind(|| {
|
||||||
|
let guard = lock.write().unwrap();
|
||||||
|
let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val);
|
||||||
|
let _guard = MappedRwLockWriteGuard::try_map::<(), _>(guard, |_| panic!());
|
||||||
|
});
|
||||||
|
|
||||||
|
match lock.try_write() {
|
||||||
|
Ok(_) => panic!(
|
||||||
|
"panicking in a MappedRwLockWriteGuard::try_map closure should poison the RwLock"
|
||||||
|
),
|
||||||
|
Err(TryLockError::WouldBlock) => panic!(
|
||||||
|
"panicking in a MappedRwLockWriteGuard::try_map closure should release the write lock"
|
||||||
|
),
|
||||||
|
Err(TryLockError::Poisoned(_)) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(lock);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user