about summary refs log tree commit diff
diff options
context:
space:
mode:
authorZachary S <zasample18+github@gmail.com>2023-10-26 13:33:54 -0500
committerZachary S <zasample18+github@gmail.com>2023-12-05 17:30:46 -0600
commit3ef4b083ac03fd25339be009e3ae525adab30d78 (patch)
tree2ae731f70922ebba3eaecab53789c28d44278b2e
parent6aebcbee0a4d232f675eb380269bb405b0a1cc32 (diff)
downloadrust-3ef4b083ac03fd25339be009e3ae525adab30d78.tar.gz
rust-3ef4b083ac03fd25339be009e3ae525adab30d78.zip
Specify behavior if the closure passed to *Guard::*map panics.
-rw-r--r--library/std/src/sync/mutex.rs78
-rw-r--r--library/std/src/sync/mutex/tests.rs63
-rw-r--r--library/std/src/sync/rwlock.rs156
-rw-r--r--library/std/src/sync/rwlock/tests.rs136
4 files changed, 364 insertions, 69 deletions
diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs
index 1b3b4a3027c..1b05247bb7c 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<U, F>(orig: Self, f: F) -> MappedMutexGuard<'a, U>
+    pub fn map<U, F>(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<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
         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 cf69813baa3..19ec096c593 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<T>(Arc<(Mutex<T>, 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 def0c8a16c7..5c4e4a784db 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<U, F>(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<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>,
         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<U, F>(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<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>,
         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<U, F>(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<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>,
         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<U, F>(orig: Self, f: F) -> MappedRwLockWriteGuard<'a, U>
+    pub fn map<U, F>(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<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
         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 0a5eb7aac02..9cc5e7a3a60 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);
+}