diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index 1b3b4a3027cd7..1b05247bb7c7c 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -612,10 +612,14 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> { F: FnOnce(&mut T) -> &mut U, U: ?Sized, { - let mut orig = ManuallyDrop::new(orig); - let value = NonNull::from(f(&mut *orig)); + // SAFETY: the conditions of `MutedGuard::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 { &mut *orig.lock.data.get() })); + let orig = ManuallyDrop::new(orig); MappedMutexGuard { - data: value, + data, inner: &orig.lock.inner, poison_flag: &orig.lock.poison, poison: orig.poison.clone(), @@ -639,16 +643,23 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> { F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { - let mut orig = ManuallyDrop::new(orig); - match f(&mut *orig).map(NonNull::from) { - Some(value) => Ok(MappedMutexGuard { - data: value, - inner: &orig.lock.inner, - poison_flag: &orig.lock.poison, - poison: orig.poison.clone(), - _variance: PhantomData, - }), - None => Err(ManuallyDrop::into_inner(orig)), + // SAFETY: the conditions of `MutexGuard::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. + match f(unsafe { &mut *orig.lock.data.get() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedMutexGuard { + 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 /// same name on the contents of the `MutexGuard` used through `Deref`. #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn map(orig: Self, f: F) -> MappedMutexGuard<'a, U> + pub fn map(mut orig: Self, f: F) -> MappedMutexGuard<'a, U> where F: FnOnce(&mut T) -> &mut U, U: ?Sized, { - let mut orig = ManuallyDrop::new(orig); - let value = NonNull::from(f(&mut *orig)); + // SAFETY: the conditions of `MutedGuard::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_mut() })); + let orig = ManuallyDrop::new(orig); MappedMutexGuard { - data: value, + data, inner: orig.inner, poison_flag: orig.poison_flag, 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`. #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map(orig: Self, f: F) -> Result, Self> + pub fn try_map(mut orig: Self, f: F) -> Result, Self> where F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { - let mut orig = ManuallyDrop::new(orig); - match f(&mut *orig).map(NonNull::from) { - Some(value) => Ok(MappedMutexGuard { - data: value, - inner: orig.inner, - poison_flag: orig.poison_flag, - poison: orig.poison.clone(), - _variance: PhantomData, - }), - None => Err(ManuallyDrop::into_inner(orig)), + // SAFETY: the conditions of `MutedGuard::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. + match f(unsafe { orig.data.as_mut() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedMutexGuard { + data, + inner: orig.inner, + poison_flag: orig.poison_flag, + poison: orig.poison.clone(), + _variance: PhantomData, + }) + } + None => Err(orig), } } } diff --git a/library/std/src/sync/mutex/tests.rs b/library/std/src/sync/mutex/tests.rs index cf69813baa3c5..19ec096c59334 100644 --- a/library/std/src/sync/mutex/tests.rs +++ b/library/std/src/sync/mutex/tests.rs @@ -1,6 +1,6 @@ use crate::sync::atomic::{AtomicUsize, Ordering}; 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; struct Packet(Arc<(Mutex, Condvar)>); @@ -264,3 +264,64 @@ fn test_mapping_mapped_guard() { drop(guard); 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); +} diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index def0c8a16c7ce..5c4e4a784dbf5 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -766,15 +766,23 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> { /// `RwLockReadGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the `RwLockReadGuard` 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")] pub fn map(orig: Self, f: F) -> MappedRwLockReadGuard<'a, U> where F: FnOnce(&T) -> &U, 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 value = NonNull::from(f(&*orig)); - MappedRwLockReadGuard { data: value, inner_lock: &orig.inner_lock } + MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } } /// 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 /// of the same name on the contents of the `RwLockReadGuard` 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")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] pub fn try_map(orig: Self, f: F) -> Result, Self> @@ -794,10 +806,17 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> { F: FnOnce(&T) -> Option<&U>, U: ?Sized, { - let orig = ManuallyDrop::new(orig); - match f(&*orig).map(NonNull::from) { - Some(value) => Ok(MappedRwLockReadGuard { data: value, inner_lock: &orig.inner_lock }), - None => Err(ManuallyDrop::into_inner(orig)), + // 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. + 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 /// methods of the same name on the contents of the `MappedRwLockReadGuard` /// 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")] pub fn map(orig: Self, f: F) -> MappedRwLockReadGuard<'a, U> where F: FnOnce(&T) -> &U, 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 value = NonNull::from(f(&*orig)); - MappedRwLockReadGuard { data: value, inner_lock: &orig.inner_lock } + MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } } /// 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 /// methods of the same name on the contents of the `MappedRwLockReadGuard` /// 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")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] pub fn try_map(orig: Self, f: F) -> Result, Self> @@ -840,10 +871,17 @@ impl<'a, T: ?Sized> MappedRwLockReadGuard<'a, T> { F: FnOnce(&T) -> Option<&U>, U: ?Sized, { - let orig = ManuallyDrop::new(orig); - match f(&*orig).map(NonNull::from) { - Some(value) => Ok(MappedRwLockReadGuard { data: value, inner_lock: &orig.inner_lock }), - None => Err(ManuallyDrop::into_inner(orig)), + // 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. + 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 /// the same name on the contents of the `RwLockWriteGuard` 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")] pub fn map(orig: Self, f: F) -> MappedRwLockWriteGuard<'a, U> where F: FnOnce(&mut T) -> &mut U, U: ?Sized, { - let mut orig = ManuallyDrop::new(orig); - let value = NonNull::from(f(&mut *orig)); + // SAFETY: the conditions of `RwLockWriteGuard::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 { &mut *orig.lock.data.get() })); + let orig = ManuallyDrop::new(orig); MappedRwLockWriteGuard { - data: value, + data, inner_lock: &orig.lock.inner, poison_flag: &orig.lock.poison, poison: orig.poison.clone(), @@ -885,6 +931,10 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> { /// `RwLockWriteGuard::try_map(...)`. A method would interfere with methods /// of the same name on the contents of the `RwLockWriteGuard` used through /// `Deref`. + /// + /// # Panics + /// + /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] pub fn try_map(orig: Self, f: F) -> Result, Self> @@ -892,16 +942,23 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> { F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { - let mut orig = ManuallyDrop::new(orig); - match f(&mut *orig).map(NonNull::from) { - Some(value) => Ok(MappedRwLockWriteGuard { - data: value, - inner_lock: &orig.lock.inner, - poison_flag: &orig.lock.poison, - poison: orig.poison.clone(), - _variance: PhantomData, - }), - None => Err(ManuallyDrop::into_inner(orig)), + // SAFETY: the conditions of `RwLockWriteGuard::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. + match f(unsafe { &mut *orig.lock.data.get() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedRwLockWriteGuard { + 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 /// methods of the same name on the contents of the `MappedRwLockWriteGuard` /// 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")] - pub fn map(orig: Self, f: F) -> MappedRwLockWriteGuard<'a, U> + pub fn map(mut orig: Self, f: F) -> MappedRwLockWriteGuard<'a, U> where F: FnOnce(&mut T) -> &mut U, U: ?Sized, { - let mut orig = ManuallyDrop::new(orig); - let value = NonNull::from(f(&mut *orig)); + // SAFETY: the conditions of `RwLockWriteGuard::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_mut() })); + let orig = ManuallyDrop::new(orig); MappedRwLockWriteGuard { - data: value, + data, inner_lock: orig.inner_lock, poison_flag: orig.poison_flag, poison: orig.poison.clone(), @@ -943,23 +1008,34 @@ impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> { /// `MappedRwLockWriteGuard::try_map(...)`. A method would interfere with /// methods of the same name on the contents of the `MappedRwLockWriteGuard` /// used through `Deref`. + /// + /// # Panics + /// + /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map(orig: Self, f: F) -> Result, Self> + pub fn try_map(mut orig: Self, f: F) -> Result, Self> where F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { - let mut orig = ManuallyDrop::new(orig); - match f(&mut *orig).map(NonNull::from) { - Some(value) => Ok(MappedRwLockWriteGuard { - data: value, - inner_lock: orig.inner_lock, - poison_flag: orig.poison_flag, - poison: orig.poison.clone(), - _variance: PhantomData, - }), - None => Err(ManuallyDrop::into_inner(orig)), + // SAFETY: the conditions of `RwLockWriteGuard::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. + match f(unsafe { orig.data.as_mut() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedRwLockWriteGuard { + data, + inner_lock: orig.inner_lock, + poison_flag: orig.poison_flag, + poison: orig.poison.clone(), + _variance: PhantomData, + }) + } + None => Err(orig), } } } diff --git a/library/std/src/sync/rwlock/tests.rs b/library/std/src/sync/rwlock/tests.rs index 0a5eb7aac023f..9cc5e7a3a60f1 100644 --- a/library/std/src/sync/rwlock/tests.rs +++ b/library/std/src/sync/rwlock/tests.rs @@ -360,3 +360,139 @@ fn test_mapping_mapped_guard() { drop(guard); 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); +}