Skip to content

Commit 3ef4b08

Browse files
committed
Specify behavior if the closure passed to *Guard::*map panics.
1 parent 6aebcbe commit 3ef4b08

File tree

4 files changed

+364
-69
lines changed

4 files changed

+364
-69
lines changed

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

+50-28
Original file line numberDiff line numberDiff line change
@@ -612,10 +612,14 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> {
612612
F: FnOnce(&mut T) -> &mut U,
613613
U: ?Sized,
614614
{
615-
let mut orig = ManuallyDrop::new(orig);
616-
let value = NonNull::from(f(&mut *orig));
615+
// SAFETY: the conditions of `MutedGuard::new` were satisfied when the original guard
616+
// was created, and have been upheld throughout `map` and/or `try_map`.
617+
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
618+
// passed to it. If the closure panics, the guard will be dropped.
619+
let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() }));
620+
let orig = ManuallyDrop::new(orig);
617621
MappedMutexGuard {
618-
data: value,
622+
data,
619623
inner: &orig.lock.inner,
620624
poison_flag: &orig.lock.poison,
621625
poison: orig.poison.clone(),
@@ -639,16 +643,23 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> {
639643
F: FnOnce(&mut T) -> Option<&mut U>,
640644
U: ?Sized,
641645
{
642-
let mut orig = ManuallyDrop::new(orig);
643-
match f(&mut *orig).map(NonNull::from) {
644-
Some(value) => Ok(MappedMutexGuard {
645-
data: value,
646-
inner: &orig.lock.inner,
647-
poison_flag: &orig.lock.poison,
648-
poison: orig.poison.clone(),
649-
_variance: PhantomData,
650-
}),
651-
None => Err(ManuallyDrop::into_inner(orig)),
646+
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
647+
// was created, and have been upheld throughout `map` and/or `try_map`.
648+
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
649+
// passed to it. If the closure panics, the guard will be dropped.
650+
match f(unsafe { &mut *orig.lock.data.get() }) {
651+
Some(data) => {
652+
let data = NonNull::from(data);
653+
let orig = ManuallyDrop::new(orig);
654+
Ok(MappedMutexGuard {
655+
data,
656+
inner: &orig.lock.inner,
657+
poison_flag: &orig.lock.poison,
658+
poison: orig.poison.clone(),
659+
_variance: PhantomData,
660+
})
661+
}
662+
None => Err(orig),
652663
}
653664
}
654665
}
@@ -704,15 +715,19 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
704715
/// `MappedMutexGuard::map(...)`. A method would interfere with methods of the
705716
/// same name on the contents of the `MutexGuard` used through `Deref`.
706717
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
707-
pub fn map<U, F>(orig: Self, f: F) -> MappedMutexGuard<'a, U>
718+
pub fn map<U, F>(mut orig: Self, f: F) -> MappedMutexGuard<'a, U>
708719
where
709720
F: FnOnce(&mut T) -> &mut U,
710721
U: ?Sized,
711722
{
712-
let mut orig = ManuallyDrop::new(orig);
713-
let value = NonNull::from(f(&mut *orig));
723+
// SAFETY: the conditions of `MutedGuard::new` were satisfied when the original guard
724+
// was created, and have been upheld throughout `map` and/or `try_map`.
725+
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
726+
// passed to it. If the closure panics, the guard will be dropped.
727+
let data = NonNull::from(f(unsafe { orig.data.as_mut() }));
728+
let orig = ManuallyDrop::new(orig);
714729
MappedMutexGuard {
715-
data: value,
730+
data,
716731
inner: orig.inner,
717732
poison_flag: orig.poison_flag,
718733
poison: orig.poison.clone(),
@@ -731,21 +746,28 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
731746
/// same name on the contents of the `MutexGuard` used through `Deref`.
732747
#[doc(alias = "filter_map")]
733748
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
734-
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
749+
pub fn try_map<U, F>(mut orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
735750
where
736751
F: FnOnce(&mut T) -> Option<&mut U>,
737752
U: ?Sized,
738753
{
739-
let mut orig = ManuallyDrop::new(orig);
740-
match f(&mut *orig).map(NonNull::from) {
741-
Some(value) => Ok(MappedMutexGuard {
742-
data: value,
743-
inner: orig.inner,
744-
poison_flag: orig.poison_flag,
745-
poison: orig.poison.clone(),
746-
_variance: PhantomData,
747-
}),
748-
None => Err(ManuallyDrop::into_inner(orig)),
754+
// SAFETY: the conditions of `MutedGuard::new` were satisfied when the original guard
755+
// was created, and have been upheld throughout `map` and/or `try_map`.
756+
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
757+
// passed to it. If the closure panics, the guard will be dropped.
758+
match f(unsafe { orig.data.as_mut() }) {
759+
Some(data) => {
760+
let data = NonNull::from(data);
761+
let orig = ManuallyDrop::new(orig);
762+
Ok(MappedMutexGuard {
763+
data,
764+
inner: orig.inner,
765+
poison_flag: orig.poison_flag,
766+
poison: orig.poison.clone(),
767+
_variance: PhantomData,
768+
})
769+
}
770+
None => Err(orig),
749771
}
750772
}
751773
}

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)