Skip to content

Commit 9be1321

Browse files
committed
Implement MappedMutexGuard.
1 parent f842d7b commit 9be1321

File tree

1 file changed

+193
-0
lines changed

1 file changed

+193
-0
lines changed

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

+193
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,43 @@ 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 = "none")]
242+
#[clippy::has_significant_drop]
243+
pub struct MappedMutexGuard<'a, T: ?Sized + 'a> {
244+
data: NonNull<T>,
245+
inner: &'a sys::Mutex,
246+
poison_flag: &'a poison::Flag,
247+
poison: poison::Guard,
248+
_variance: PhantomData<&'a mut T>,
249+
}
250+
251+
#[unstable(feature = "mapped_lock_guards", issue = "none")]
252+
impl<T: ?Sized> !Send for MappedMutexGuard<'_, T> {}
253+
#[unstable(feature = "mapped_lock_guards", issue = "none")]
254+
unsafe impl<T: ?Sized + Sync> Sync for MappedMutexGuard<'_, T> {}
255+
216256
impl<T> Mutex<T> {
217257
/// Creates a new mutex in an unlocked state ready for use.
218258
///
@@ -552,3 +592,156 @@ pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex {
552592
pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag {
553593
&guard.lock.poison
554594
}
595+
596+
impl<'a, T: ?Sized> MutexGuard<'a, T> {
597+
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g.
598+
/// an enum variant.
599+
///
600+
/// The `Mutex` is already locked, so this cannot fail.
601+
///
602+
/// This is an associated function that needs to be used as
603+
/// `MutexGuard::map(...)`. A method would interfere with methods of the
604+
/// same name on the contents of the `MutexGuard` used through `Deref`.
605+
#[unstable(feature = "mapped_lock_guards", issue = "none")]
606+
pub fn map<U, F>(orig: Self, f: F) -> MappedMutexGuard<'a, U>
607+
where
608+
F: FnOnce(&mut T) -> &mut U,
609+
U: ?Sized,
610+
{
611+
let mut orig = ManuallyDrop::new(orig);
612+
let value = NonNull::from(f(&mut *orig));
613+
MappedMutexGuard {
614+
data: value,
615+
inner: &orig.lock.inner,
616+
poison_flag: &orig.lock.poison,
617+
poison: orig.poison.clone(),
618+
_variance: PhantomData,
619+
}
620+
}
621+
622+
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The
623+
/// original guard is returned as an `Err(...)` if the closure returns
624+
/// `None`.
625+
///
626+
/// The `Mutex` is already locked, so this cannot fail.
627+
///
628+
/// This is an associated function that needs to be used as
629+
/// `MutexGuard::try_map(...)`. A method would interfere with methods of the
630+
/// same name on the contents of the `MutexGuard` used through `Deref`.
631+
#[doc(alias = "filter_map")]
632+
#[unstable(feature = "mapped_lock_guards", issue = "none")]
633+
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
634+
where
635+
F: FnOnce(&mut T) -> Option<&mut U>,
636+
U: ?Sized,
637+
{
638+
let mut orig = ManuallyDrop::new(orig);
639+
match f(&mut *orig).map(NonNull::from) {
640+
Some(value) => Ok(MappedMutexGuard {
641+
data: value,
642+
inner: &orig.lock.inner,
643+
poison_flag: &orig.lock.poison,
644+
poison: orig.poison.clone(),
645+
_variance: PhantomData,
646+
}),
647+
None => Err(ManuallyDrop::into_inner(orig)),
648+
}
649+
}
650+
}
651+
652+
#[unstable(feature = "mapped_lock_guards", issue = "none")]
653+
impl<T: ?Sized> Deref for MappedMutexGuard<'_, T> {
654+
type Target = T;
655+
656+
fn deref(&self) -> &T {
657+
unsafe { self.data.as_ref() }
658+
}
659+
}
660+
661+
#[unstable(feature = "mapped_lock_guards", issue = "none")]
662+
impl<T: ?Sized> DerefMut for MappedMutexGuard<'_, T> {
663+
fn deref_mut(&mut self) -> &mut T {
664+
unsafe { self.data.as_mut() }
665+
}
666+
}
667+
668+
#[unstable(feature = "mapped_lock_guards", issue = "none")]
669+
impl<T: ?Sized> Drop for MappedMutexGuard<'_, T> {
670+
#[inline]
671+
fn drop(&mut self) {
672+
unsafe {
673+
self.poison_flag.done(&self.poison);
674+
self.inner.unlock();
675+
}
676+
}
677+
}
678+
679+
#[unstable(feature = "mapped_lock_guards", issue = "none")]
680+
impl<T: ?Sized + fmt::Debug> fmt::Debug for MappedMutexGuard<'_, T> {
681+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
682+
fmt::Debug::fmt(&**self, f)
683+
}
684+
}
685+
686+
#[unstable(feature = "mapped_lock_guards", issue = "none")]
687+
impl<T: ?Sized + fmt::Display> fmt::Display for MappedMutexGuard<'_, T> {
688+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
689+
(**self).fmt(f)
690+
}
691+
}
692+
693+
impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
694+
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g.
695+
/// an enum variant.
696+
///
697+
/// The `Mutex` is already locked, so this cannot fail.
698+
///
699+
/// This is an associated function that needs to be used as
700+
/// `MutexGuard::map(...)`. A method would interfere with methods of the
701+
/// same name on the contents of the `MutexGuard` used through `Deref`.
702+
#[unstable(feature = "mapped_lock_guards", issue = "none")]
703+
pub fn map<U, F>(orig: Self, f: F) -> MappedMutexGuard<'a, U>
704+
where
705+
F: FnOnce(&mut T) -> &mut U,
706+
U: ?Sized,
707+
{
708+
let mut orig = ManuallyDrop::new(orig);
709+
let value = NonNull::from(f(&mut *orig));
710+
MappedMutexGuard {
711+
data: value,
712+
inner: orig.inner,
713+
poison_flag: orig.poison_flag,
714+
poison: orig.poison.clone(),
715+
_variance: PhantomData,
716+
}
717+
}
718+
719+
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The
720+
/// original guard is returned as an `Err(...)` if the closure returns
721+
/// `None`.
722+
///
723+
/// The `Mutex` is already locked, so this cannot fail.
724+
///
725+
/// This is an associated function that needs to be used as
726+
/// `MutexGuard::try_map(...)`. A method would interfere with methods of the
727+
/// same name on the contents of the `MutexGuard` used through `Deref`.
728+
#[doc(alias = "filter_map")]
729+
#[unstable(feature = "mapped_lock_guards", issue = "none")]
730+
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
731+
where
732+
F: FnOnce(&mut T) -> Option<&mut U>,
733+
U: ?Sized,
734+
{
735+
let mut orig = ManuallyDrop::new(orig);
736+
match f(&mut *orig).map(NonNull::from) {
737+
Some(value) => Ok(MappedMutexGuard {
738+
data: value,
739+
inner: orig.inner,
740+
poison_flag: orig.poison_flag,
741+
poison: orig.poison.clone(),
742+
_variance: PhantomData,
743+
}),
744+
None => Err(ManuallyDrop::into_inner(orig)),
745+
}
746+
}
747+
}

0 commit comments

Comments
 (0)