Skip to content

Commit 361e9b3

Browse files
authored
Merge branch 'master' into issue-121502-fix
2 parents 748c615 + 710048f commit 361e9b3

26 files changed

+1445
-326
lines changed

Diff for: Cargo.lock

+3-2
Original file line numberDiff line numberDiff line change
@@ -3308,13 +3308,14 @@ dependencies = [
33083308

33093309
[[package]]
33103310
name = "rustc-build-sysroot"
3311-
version = "0.4.4"
3311+
version = "0.4.5"
33123312
source = "registry+https://github.com/rust-lang/crates.io-index"
3313-
checksum = "39dcf8d82b1f79a179bdb284dc44db440a9666eefa5a6df5ef282d6db930d544"
3313+
checksum = "a26170e1d79ea32f7ccec3188dd13cfc1f18c82764a9cbc1071667c0f865a4ea"
33143314
dependencies = [
33153315
"anyhow",
33163316
"rustc_version",
33173317
"tempfile",
3318+
"walkdir",
33183319
]
33193320

33203321
[[package]]

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

+4
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,17 @@ pub use core::sync::Exclusive;
165165
pub use self::barrier::{Barrier, BarrierWaitResult};
166166
#[stable(feature = "rust1", since = "1.0.0")]
167167
pub use self::condvar::{Condvar, WaitTimeoutResult};
168+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
169+
pub use self::mutex::MappedMutexGuard;
168170
#[stable(feature = "rust1", since = "1.0.0")]
169171
pub use self::mutex::{Mutex, MutexGuard};
170172
#[stable(feature = "rust1", since = "1.0.0")]
171173
#[allow(deprecated)]
172174
pub use self::once::{Once, OnceState, ONCE_INIT};
173175
#[stable(feature = "rust1", since = "1.0.0")]
174176
pub use self::poison::{LockResult, PoisonError, TryLockError, TryLockResult};
177+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
178+
pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard};
175179
#[stable(feature = "rust1", since = "1.0.0")]
176180
pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
177181

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

+219
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ mod tests;
33

44
use crate::cell::UnsafeCell;
55
use crate::fmt;
6+
use crate::marker::PhantomData;
7+
use crate::mem::ManuallyDrop;
68
use crate::ops::{Deref, DerefMut};
9+
use crate::ptr::NonNull;
710
use crate::sync::{poison, LockResult, TryLockError, TryLockResult};
811
use crate::sys::locks as sys;
912

@@ -213,6 +216,47 @@ impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
213216
#[stable(feature = "mutexguard", since = "1.19.0")]
214217
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
215218

219+
/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a
220+
/// subfield of the protected data. When this structure is dropped (falls out
221+
/// of scope), the lock will be unlocked.
222+
///
223+
/// The main difference between `MappedMutexGuard` and [`MutexGuard`] is that the
224+
/// former cannot be used with [`Condvar`], since that
225+
/// could introduce soundness issues if the locked object is modified by another
226+
/// thread while the `Mutex` is unlocked.
227+
///
228+
/// The data protected by the mutex can be accessed through this guard via its
229+
/// [`Deref`] and [`DerefMut`] implementations.
230+
///
231+
/// This structure is created by the [`map`] and [`try_map`] methods on
232+
/// [`MutexGuard`].
233+
///
234+
/// [`map`]: MutexGuard::map
235+
/// [`try_map`]: MutexGuard::try_map
236+
/// [`Condvar`]: crate::sync::Condvar
237+
#[must_use = "if unused the Mutex will immediately unlock"]
238+
#[must_not_suspend = "holding a MappedMutexGuard across suspend \
239+
points can cause deadlocks, delays, \
240+
and cause Futures to not implement `Send`"]
241+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
242+
#[clippy::has_significant_drop]
243+
pub struct MappedMutexGuard<'a, T: ?Sized + 'a> {
244+
// NB: we use a pointer instead of `&'a mut T` to avoid `noalias` violations, because a
245+
// `MappedMutexGuard` argument doesn't hold uniqueness for its whole scope, only until it drops.
246+
// `NonNull` is covariant over `T`, so we add a `PhantomData<&'a mut T>` field
247+
// below for the correct variance over `T` (invariance).
248+
data: NonNull<T>,
249+
inner: &'a sys::Mutex,
250+
poison_flag: &'a poison::Flag,
251+
poison: poison::Guard,
252+
_variance: PhantomData<&'a mut T>,
253+
}
254+
255+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
256+
impl<T: ?Sized> !Send for MappedMutexGuard<'_, T> {}
257+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
258+
unsafe impl<T: ?Sized + Sync> Sync for MappedMutexGuard<'_, T> {}
259+
216260
impl<T> Mutex<T> {
217261
/// Creates a new mutex in an unlocked state ready for use.
218262
///
@@ -550,3 +594,178 @@ pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex {
550594
pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag {
551595
&guard.lock.poison
552596
}
597+
598+
impl<'a, T: ?Sized> MutexGuard<'a, T> {
599+
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g.
600+
/// an enum variant.
601+
///
602+
/// The `Mutex` is already locked, so this cannot fail.
603+
///
604+
/// This is an associated function that needs to be used as
605+
/// `MutexGuard::map(...)`. A method would interfere with methods of the
606+
/// same name on the contents of the `MutexGuard` used through `Deref`.
607+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
608+
pub fn map<U, F>(orig: Self, f: F) -> MappedMutexGuard<'a, U>
609+
where
610+
F: FnOnce(&mut T) -> &mut U,
611+
U: ?Sized,
612+
{
613+
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
614+
// was created, and have been upheld throughout `map` and/or `try_map`.
615+
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
616+
// passed to it. If the closure panics, the guard will be dropped.
617+
let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() }));
618+
let orig = ManuallyDrop::new(orig);
619+
MappedMutexGuard {
620+
data,
621+
inner: &orig.lock.inner,
622+
poison_flag: &orig.lock.poison,
623+
poison: orig.poison.clone(),
624+
_variance: PhantomData,
625+
}
626+
}
627+
628+
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The
629+
/// original guard is returned as an `Err(...)` if the closure returns
630+
/// `None`.
631+
///
632+
/// The `Mutex` is already locked, so this cannot fail.
633+
///
634+
/// This is an associated function that needs to be used as
635+
/// `MutexGuard::try_map(...)`. A method would interfere with methods of the
636+
/// same name on the contents of the `MutexGuard` used through `Deref`.
637+
#[doc(alias = "filter_map")]
638+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
639+
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
640+
where
641+
F: FnOnce(&mut T) -> Option<&mut U>,
642+
U: ?Sized,
643+
{
644+
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
645+
// was created, and have been upheld throughout `map` and/or `try_map`.
646+
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
647+
// passed to it. If the closure panics, the guard will be dropped.
648+
match f(unsafe { &mut *orig.lock.data.get() }) {
649+
Some(data) => {
650+
let data = NonNull::from(data);
651+
let orig = ManuallyDrop::new(orig);
652+
Ok(MappedMutexGuard {
653+
data,
654+
inner: &orig.lock.inner,
655+
poison_flag: &orig.lock.poison,
656+
poison: orig.poison.clone(),
657+
_variance: PhantomData,
658+
})
659+
}
660+
None => Err(orig),
661+
}
662+
}
663+
}
664+
665+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
666+
impl<T: ?Sized> Deref for MappedMutexGuard<'_, T> {
667+
type Target = T;
668+
669+
fn deref(&self) -> &T {
670+
unsafe { self.data.as_ref() }
671+
}
672+
}
673+
674+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
675+
impl<T: ?Sized> DerefMut for MappedMutexGuard<'_, T> {
676+
fn deref_mut(&mut self) -> &mut T {
677+
unsafe { self.data.as_mut() }
678+
}
679+
}
680+
681+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
682+
impl<T: ?Sized> Drop for MappedMutexGuard<'_, T> {
683+
#[inline]
684+
fn drop(&mut self) {
685+
unsafe {
686+
self.poison_flag.done(&self.poison);
687+
self.inner.unlock();
688+
}
689+
}
690+
}
691+
692+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
693+
impl<T: ?Sized + fmt::Debug> fmt::Debug for MappedMutexGuard<'_, T> {
694+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
695+
fmt::Debug::fmt(&**self, f)
696+
}
697+
}
698+
699+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
700+
impl<T: ?Sized + fmt::Display> fmt::Display for MappedMutexGuard<'_, T> {
701+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
702+
(**self).fmt(f)
703+
}
704+
}
705+
706+
impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
707+
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g.
708+
/// an enum variant.
709+
///
710+
/// The `Mutex` is already locked, so this cannot fail.
711+
///
712+
/// This is an associated function that needs to be used as
713+
/// `MappedMutexGuard::map(...)`. A method would interfere with methods of the
714+
/// same name on the contents of the `MutexGuard` used through `Deref`.
715+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
716+
pub fn map<U, F>(mut orig: Self, f: F) -> MappedMutexGuard<'a, U>
717+
where
718+
F: FnOnce(&mut T) -> &mut U,
719+
U: ?Sized,
720+
{
721+
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
722+
// was created, and have been upheld throughout `map` and/or `try_map`.
723+
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
724+
// passed to it. If the closure panics, the guard will be dropped.
725+
let data = NonNull::from(f(unsafe { orig.data.as_mut() }));
726+
let orig = ManuallyDrop::new(orig);
727+
MappedMutexGuard {
728+
data,
729+
inner: orig.inner,
730+
poison_flag: orig.poison_flag,
731+
poison: orig.poison.clone(),
732+
_variance: PhantomData,
733+
}
734+
}
735+
736+
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The
737+
/// original guard is returned as an `Err(...)` if the closure returns
738+
/// `None`.
739+
///
740+
/// The `Mutex` is already locked, so this cannot fail.
741+
///
742+
/// This is an associated function that needs to be used as
743+
/// `MappedMutexGuard::try_map(...)`. A method would interfere with methods of the
744+
/// same name on the contents of the `MutexGuard` used through `Deref`.
745+
#[doc(alias = "filter_map")]
746+
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
747+
pub fn try_map<U, F>(mut orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
748+
where
749+
F: FnOnce(&mut T) -> Option<&mut U>,
750+
U: ?Sized,
751+
{
752+
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
753+
// was created, and have been upheld throughout `map` and/or `try_map`.
754+
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
755+
// passed to it. If the closure panics, the guard will be dropped.
756+
match f(unsafe { orig.data.as_mut() }) {
757+
Some(data) => {
758+
let data = NonNull::from(data);
759+
let orig = ManuallyDrop::new(orig);
760+
Ok(MappedMutexGuard {
761+
data,
762+
inner: orig.inner,
763+
poison_flag: orig.poison_flag,
764+
poison: orig.poison.clone(),
765+
_variance: PhantomData,
766+
})
767+
}
768+
None => Err(orig),
769+
}
770+
}
771+
}

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

+90-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, Mutex};
3+
use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError};
44
use crate::thread;
55

66
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
@@ -188,6 +188,21 @@ fn test_mutex_arc_poison() {
188188
assert!(arc.is_poisoned());
189189
}
190190

191+
#[test]
192+
fn test_mutex_arc_poison_mapped() {
193+
let arc = Arc::new(Mutex::new(1));
194+
assert!(!arc.is_poisoned());
195+
let arc2 = arc.clone();
196+
let _ = thread::spawn(move || {
197+
let lock = arc2.lock().unwrap();
198+
let lock = MutexGuard::map(lock, |val| val);
199+
assert_eq!(*lock, 2); // deliberate assertion failure to poison the mutex
200+
})
201+
.join();
202+
assert!(arc.lock().is_err());
203+
assert!(arc.is_poisoned());
204+
}
205+
191206
#[test]
192207
fn test_mutex_arc_nested() {
193208
// Tests nested mutexes and access
@@ -236,3 +251,77 @@ fn test_mutex_unsized() {
236251
let comp: &[i32] = &[4, 2, 5];
237252
assert_eq!(&*mutex.lock().unwrap(), comp);
238253
}
254+
255+
#[test]
256+
fn test_mapping_mapped_guard() {
257+
let arr = [0; 4];
258+
let mut lock = Mutex::new(arr);
259+
let guard = lock.lock().unwrap();
260+
let guard = MutexGuard::map(guard, |arr| &mut arr[..2]);
261+
let mut guard = MappedMutexGuard::map(guard, |slice| &mut slice[1..]);
262+
assert_eq!(guard.len(), 1);
263+
guard[0] = 42;
264+
drop(guard);
265+
assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]);
266+
}
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+
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ impl Flag {
7878
}
7979
}
8080

81+
#[derive(Clone)]
8182
pub struct Guard {
8283
#[cfg(panic = "unwind")]
8384
panicking: bool,

0 commit comments

Comments
 (0)