Skip to content

Commit 9d81136

Browse files
committed
Specify behavior if the closure passed to *Guard::*map panics.
1 parent b72eb06 commit 9d81136

File tree

4 files changed

+361
-66
lines changed

4 files changed

+361
-66
lines changed

Diff for: library/std/src/sync/mutex.rs

+49-27
Original file line numberDiff line numberDiff line change
@@ -618,10 +618,14 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> {
618618
F: FnOnce(&mut T) -> &mut U,
619619
U: ?Sized,
620620
{
621-
let mut orig = ManuallyDrop::new(orig);
622-
let value = NonNull::from(f(&mut *orig));
621+
// SAFETY: the conditions of `MutedGuard::new` were satisfied when the original guard
622+
// was created, and have been upheld throughout `map` and/or `try_map`.
623+
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
624+
// passed to it. If the closure panics, the guard will be dropped.
625+
let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() }));
626+
let orig = ManuallyDrop::new(orig);
623627
MappedMutexGuard {
624-
data: value,
628+
data,
625629
inner_state: &orig.lock.state,
626630
poison: orig.poison.clone(),
627631
_variance: PhantomData,
@@ -644,15 +648,22 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> {
644648
F: FnOnce(&mut T) -> Option<&mut U>,
645649
U: ?Sized,
646650
{
647-
let mut orig = ManuallyDrop::new(orig);
648-
match f(&mut *orig).map(NonNull::from) {
649-
Some(value) => Ok(MappedMutexGuard {
650-
data: value,
651-
inner_state: &orig.lock.state,
652-
poison: orig.poison.clone(),
653-
_variance: PhantomData,
654-
}),
655-
None => Err(ManuallyDrop::into_inner(orig)),
651+
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
652+
// was created, and have been upheld throughout `map` and/or `try_map`.
653+
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
654+
// passed to it. If the closure panics, the guard will be dropped.
655+
match f(unsafe { &mut *orig.lock.data.get() }) {
656+
Some(data) => {
657+
let data = NonNull::from(data);
658+
let orig = ManuallyDrop::new(orig);
659+
Ok(MappedMutexGuard {
660+
data,
661+
inner_state: &orig.lock.state,
662+
poison: orig.poison.clone(),
663+
_variance: PhantomData,
664+
})
665+
}
666+
None => Err(orig),
656667
}
657668
}
658669
}
@@ -708,16 +719,20 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
708719
/// `MappedMutexGuard::map(...)`. A method would interfere with methods of the
709720
/// same name on the contents of the `MutexGuard` used through `Deref`.
710721
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
711-
pub fn map<U, F>(orig: Self, f: F) -> MappedMutexGuard<'a, U>
722+
pub fn map<U, F>(mut orig: Self, f: F) -> MappedMutexGuard<'a, U>
712723
where
713724
F: FnOnce(&mut T) -> &mut U,
714725
U: ?Sized,
715726
{
716-
let mut orig = ManuallyDrop::new(orig);
717-
let value = NonNull::from(f(&mut *orig));
727+
// SAFETY: the conditions of `MutedGuard::new` were satisfied when the original guard
728+
// was created, and have been upheld throughout `map` and/or `try_map`.
729+
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
730+
// passed to it. If the closure panics, the guard will be dropped.
731+
let data = NonNull::from(f(unsafe { orig.data.as_mut() }));
732+
let orig = ManuallyDrop::new(orig);
718733
MappedMutexGuard {
719-
data: value,
720-
inner_state: &orig.inner_state,
734+
data,
735+
inner_state: orig.inner_state,
721736
poison: orig.poison.clone(),
722737
_variance: PhantomData,
723738
}
@@ -734,20 +749,27 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
734749
/// same name on the contents of the `MutexGuard` used through `Deref`.
735750
#[doc(alias = "filter_map")]
736751
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
737-
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
752+
pub fn try_map<U, F>(mut orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
738753
where
739754
F: FnOnce(&mut T) -> Option<&mut U>,
740755
U: ?Sized,
741756
{
742-
let mut orig = ManuallyDrop::new(orig);
743-
match f(&mut *orig).map(NonNull::from) {
744-
Some(value) => Ok(MappedMutexGuard {
745-
data: value,
746-
inner_state: &orig.inner_state,
747-
poison: orig.poison.clone(),
748-
_variance: PhantomData,
749-
}),
750-
None => Err(ManuallyDrop::into_inner(orig)),
757+
// SAFETY: the conditions of `MutedGuard::new` were satisfied when the original guard
758+
// was created, and have been upheld throughout `map` and/or `try_map`.
759+
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
760+
// passed to it. If the closure panics, the guard will be dropped.
761+
match f(unsafe { orig.data.as_mut() }) {
762+
Some(data) => {
763+
let data = NonNull::from(data);
764+
let orig = ManuallyDrop::new(orig);
765+
Ok(MappedMutexGuard {
766+
data,
767+
inner_state: &orig.inner_state,
768+
poison: orig.poison.clone(),
769+
_variance: PhantomData,
770+
})
771+
}
772+
None => Err(orig),
751773
}
752774
}
753775
}

Diff for: library/std/src/sync/mutex/tests.rs

+62-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::sync::atomic::{AtomicUsize, Ordering};
22
use crate::sync::mpsc::channel;
3-
use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard};
3+
use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError};
44
use crate::thread;
55

66
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
@@ -264,3 +264,64 @@ fn test_mapping_mapped_guard() {
264264
drop(guard);
265265
assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]);
266266
}
267+
268+
#[test]
269+
fn panic_while_mapping_unlocked_poison() {
270+
let lock = Mutex::new(());
271+
272+
let _ = crate::panic::catch_unwind(|| {
273+
let guard = lock.lock().unwrap();
274+
let _guard = MutexGuard::map::<(), _>(guard, |_| panic!());
275+
});
276+
277+
match lock.try_lock() {
278+
Ok(_) => panic!("panicking in a MutexGuard::map closure should poison the Mutex"),
279+
Err(TryLockError::WouldBlock) => {
280+
panic!("panicking in a MutexGuard::map closure should unlock the mutex")
281+
}
282+
Err(TryLockError::Poisoned(_)) => {}
283+
}
284+
285+
let _ = crate::panic::catch_unwind(|| {
286+
let guard = lock.lock().unwrap();
287+
let _guard = MutexGuard::try_map::<(), _>(guard, |_| panic!());
288+
});
289+
290+
match lock.try_lock() {
291+
Ok(_) => panic!("panicking in a MutexGuard::try_map closure should poison the Mutex"),
292+
Err(TryLockError::WouldBlock) => {
293+
panic!("panicking in a MutexGuard::try_map closure should unlock the mutex")
294+
}
295+
Err(TryLockError::Poisoned(_)) => {}
296+
}
297+
298+
let _ = crate::panic::catch_unwind(|| {
299+
let guard = lock.lock().unwrap();
300+
let guard = MutexGuard::map::<(), _>(guard, |val| val);
301+
let _guard = MappedMutexGuard::map::<(), _>(guard, |_| panic!());
302+
});
303+
304+
match lock.try_lock() {
305+
Ok(_) => panic!("panicking in a MappedMutexGuard::map closure should poison the Mutex"),
306+
Err(TryLockError::WouldBlock) => {
307+
panic!("panicking in a MappedMutexGuard::map closure should unlock the mutex")
308+
}
309+
Err(TryLockError::Poisoned(_)) => {}
310+
}
311+
312+
let _ = crate::panic::catch_unwind(|| {
313+
let guard = lock.lock().unwrap();
314+
let guard = MutexGuard::map::<(), _>(guard, |val| val);
315+
let _guard = MappedMutexGuard::try_map::<(), _>(guard, |_| panic!());
316+
});
317+
318+
match lock.try_lock() {
319+
Ok(_) => panic!("panicking in a MappedMutexGuard::try_map closure should poison the Mutex"),
320+
Err(TryLockError::WouldBlock) => {
321+
panic!("panicking in a MappedMutexGuard::try_map closure should unlock the mutex")
322+
}
323+
Err(TryLockError::Poisoned(_)) => {}
324+
}
325+
326+
drop(lock);
327+
}

0 commit comments

Comments
 (0)