@@ -3,7 +3,10 @@ mod tests;
33
44use crate :: cell:: UnsafeCell ;
55use crate :: fmt;
6+ use crate :: marker:: PhantomData ;
7+ use crate :: mem:: ManuallyDrop ;
68use crate :: ops:: { Deref , DerefMut } ;
9+ use crate :: ptr:: NonNull ;
710use crate :: sync:: { poison, LockResult , TryLockError , TryLockResult } ;
811use 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" ) ]
214217unsafe 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+
216260impl < 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 {
550594pub 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+ }
0 commit comments