77
88use std:: fmt;
99
10- #[ cfg( not( parallel_compiler) ) ]
11- pub use disabled:: * ;
1210#[ cfg( parallel_compiler) ]
13- pub use enabled:: * ;
11+ pub use maybe_sync:: * ;
12+ #[ cfg( not( parallel_compiler) ) ]
13+ pub use no_sync:: * ;
1414
1515#[ derive( Clone , Copy , PartialEq ) ]
16- pub enum Assume {
16+ pub enum Mode {
1717 NoSync ,
1818 Sync ,
1919}
2020
21- mod enabled {
22- use super :: Assume ;
21+ mod maybe_sync {
22+ use super :: Mode ;
2323 use crate :: sync:: mode;
2424 #[ cfg( parallel_compiler) ]
2525 use crate :: sync:: { DynSend , DynSync } ;
2626 use parking_lot:: lock_api:: RawMutex as _;
2727 use parking_lot:: RawMutex ;
2828 use std:: cell:: Cell ;
2929 use std:: cell:: UnsafeCell ;
30- use std:: hint:: unreachable_unchecked;
3130 use std:: intrinsics:: unlikely;
3231 use std:: marker:: PhantomData ;
32+ use std:: mem:: ManuallyDrop ;
3333 use std:: ops:: { Deref , DerefMut } ;
3434
3535 /// A guard holding mutable access to a `Lock` which is in a locked state.
@@ -40,7 +40,7 @@ mod enabled {
4040
4141 /// The syncronization mode of the lock. This is explicitly passed to let LLVM relate it
4242 /// to the original lock operation.
43- assume : Assume ,
43+ mode : Mode ,
4444 }
4545
4646 impl < ' a , T : ' a > Deref for LockGuard < ' a , T > {
@@ -64,36 +64,26 @@ mod enabled {
6464 impl < ' a , T : ' a > Drop for LockGuard < ' a , T > {
6565 #[ inline]
6666 fn drop ( & mut self ) {
67- // SAFETY (dispatch): We get `self.assume` from the lock operation so it is consistent
68- // with the lock state.
69- // SAFETY (unlock): We know that the lock is locked as this type is a proof of that.
70- unsafe {
71- self . lock . dispatch (
72- self . assume ,
73- |cell| {
74- debug_assert_eq ! ( cell. get( ) , true ) ;
75- cell. set ( false ) ;
76- Some ( ( ) )
77- } ,
78- |lock| lock. unlock ( ) ,
79- ) ;
80- } ;
67+ // SAFETY (union access): We get `self.mode` from the lock operation so it is consistent
68+ // with the `lock.mode` state. This means we access the right union fields.
69+ match self . mode {
70+ Mode :: NoSync => {
71+ let cell = unsafe { & self . lock . mode_union . no_sync } ;
72+ debug_assert_eq ! ( cell. get( ) , true ) ;
73+ cell. set ( false ) ;
74+ }
75+ // SAFETY (unlock): We know that the lock is locked as this type is a proof of that.
76+ Mode :: Sync => unsafe { self . lock . mode_union . sync . unlock ( ) } ,
77+ }
8178 }
8279 }
8380
84- enum LockMode {
85- NoSync ( Cell < bool > ) ,
86- Sync ( RawMutex ) ,
87- }
81+ union ModeUnion {
82+ /// Indicates if the cell is locked. Only used if `Lock.mode` is `NoSync`.
83+ no_sync : ManuallyDrop < Cell < bool > > ,
8884
89- impl LockMode {
90- #[ inline( always) ]
91- fn to_assume ( & self ) -> Assume {
92- match self {
93- LockMode :: NoSync ( ..) => Assume :: NoSync ,
94- LockMode :: Sync ( ..) => Assume :: Sync ,
95- }
96- }
85+ /// A lock implementation that's only used if `Lock.mode` is `Sync`.
86+ sync : ManuallyDrop < RawMutex > ,
9787 }
9888
9989 /// The value representing a locked state for the `Cell`.
@@ -102,23 +92,26 @@ mod enabled {
10292 /// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
10393 /// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`.
10494 pub struct Lock < T > {
105- mode : LockMode ,
95+ /// Indicates if synchronization is used via `mode_union.sync` if it's `Sync`, or if a
96+ /// not thread safe cell is used via `mode_union.no_sync` if it's `NoSync`.
97+ /// This is set on initialization and never changed.
98+ mode : Mode ,
99+
100+ mode_union : ModeUnion ,
106101 data : UnsafeCell < T > ,
107102 }
108103
109104 impl < T > Lock < T > {
110105 #[ inline( always) ]
111106 pub fn new ( inner : T ) -> Self {
112- Lock {
113- mode : if unlikely ( mode:: might_be_dyn_thread_safe ( ) ) {
114- // Create the lock with synchronization enabled using the `RawMutex` type.
115- LockMode :: Sync ( RawMutex :: INIT )
116- } else {
117- // Create the lock with synchronization disabled.
118- LockMode :: NoSync ( Cell :: new ( !LOCKED ) )
119- } ,
120- data : UnsafeCell :: new ( inner) ,
121- }
107+ let ( mode, mode_union) = if unlikely ( mode:: might_be_dyn_thread_safe ( ) ) {
108+ // Create the lock with synchronization enabled using the `RawMutex` type.
109+ ( Mode :: Sync , ModeUnion { sync : ManuallyDrop :: new ( RawMutex :: INIT ) } )
110+ } else {
111+ // Create the lock with synchronization disabled.
112+ ( Mode :: NoSync , ModeUnion { no_sync : ManuallyDrop :: new ( Cell :: new ( !LOCKED ) ) } )
113+ } ;
114+ Lock { mode, mode_union, data : UnsafeCell :: new ( inner) }
122115 }
123116
124117 #[ inline( always) ]
@@ -131,79 +124,58 @@ mod enabled {
131124 self . data . get_mut ( )
132125 }
133126
134- /// This dispatches on the `LockMode` and gives access to its variants depending on
135- /// `assume`. If `no_sync` returns `None` this will panic.
127+ #[ inline( always) ]
128+ pub fn try_lock ( & self ) -> Option < LockGuard < ' _ , T > > {
129+ let mode = self . mode ;
130+ // SAFETY: This is safe since the union fields are used in accordance with `self.mode`.
131+ match mode {
132+ Mode :: NoSync => {
133+ let cell = unsafe { & self . mode_union . no_sync } ;
134+ let was_unlocked = cell. get ( ) != LOCKED ;
135+ if was_unlocked {
136+ cell. set ( LOCKED ) ;
137+ }
138+ was_unlocked
139+ }
140+ Mode :: Sync => unsafe { self . mode_union . sync . try_lock ( ) } ,
141+ }
142+ . then ( || LockGuard { lock : self , marker : PhantomData , mode } )
143+ }
144+
145+ /// This acquires the lock assuming syncronization is in a specific mode.
136146 ///
137147 /// Safety
138- /// This method must only be called if `might_be_dyn_thread_safe` on lock creation matches
139- /// matches the `assume` argument .
148+ /// This method must only be called with `Mode::Sync` if `might_be_dyn_thread_safe` was
149+ /// true on lock creation .
140150 #[ inline( always) ]
141151 #[ track_caller]
142- unsafe fn dispatch < R > (
143- & self ,
144- assume : Assume ,
145- no_sync : impl FnOnce ( & Cell < bool > ) -> Option < R > ,
146- sync : impl FnOnce ( & RawMutex ) -> R ,
147- ) -> R {
152+ pub unsafe fn lock_assume ( & self , mode : Mode ) -> LockGuard < ' _ , T > {
148153 #[ inline( never) ]
149154 #[ track_caller]
150155 #[ cold]
151156 fn lock_held ( ) -> ! {
152157 panic ! ( "lock was already held" )
153158 }
154159
155- match assume {
156- Assume :: NoSync => {
157- let LockMode :: NoSync ( cell) = & self . mode else {
158- unsafe { unreachable_unchecked ( ) }
159- } ;
160- if let Some ( v) = no_sync ( cell) {
161- v
162- } else {
163- // Call this here instead of in `no_sync` so `track_caller` gets properly
164- // passed along.
165- lock_held ( )
160+ // SAFETY: This is safe since the union fields are used in accordance with `mode`
161+ // which also must match `self.mode` due to the safety precondition.
162+ unsafe {
163+ match mode {
164+ Mode :: NoSync => {
165+ if unlikely ( self . mode_union . no_sync . replace ( LOCKED ) == LOCKED ) {
166+ lock_held ( )
167+ }
166168 }
169+ Mode :: Sync => self . mode_union . sync . lock ( ) ,
167170 }
168- Assume :: Sync => {
169- let LockMode :: Sync ( lock) = & self . mode else {
170- unsafe { unreachable_unchecked ( ) }
171- } ;
172- sync ( lock)
173- }
174- }
175- }
176-
177- #[ inline( always) ]
178- pub fn try_lock ( & self ) -> Option < LockGuard < ' _ , T > > {
179- let assume = self . mode . to_assume ( ) ;
180- unsafe {
181- self . dispatch (
182- assume,
183- |cell| Some ( ( cell. get ( ) != LOCKED ) . then ( || cell. set ( LOCKED ) ) . is_some ( ) ) ,
184- RawMutex :: try_lock,
185- )
186- . then ( || LockGuard { lock : self , marker : PhantomData , assume } )
187- }
188- }
189-
190- #[ inline( always) ]
191- #[ track_caller]
192- pub unsafe fn lock_assume ( & self , assume : Assume ) -> LockGuard < ' _ , T > {
193- unsafe {
194- self . dispatch (
195- assume,
196- |cell| ( cell. replace ( LOCKED ) != LOCKED ) . then ( || ( ) ) ,
197- RawMutex :: lock,
198- ) ;
199- LockGuard { lock : self , marker : PhantomData , assume }
200171 }
172+ LockGuard { lock : self , marker : PhantomData , mode }
201173 }
202174
203175 #[ inline( always) ]
204176 #[ track_caller]
205177 pub fn lock ( & self ) -> LockGuard < ' _ , T > {
206- unsafe { self . lock_assume ( self . mode . to_assume ( ) ) }
178+ unsafe { self . lock_assume ( self . mode ) }
207179 }
208180 }
209181
@@ -213,8 +185,8 @@ mod enabled {
213185 unsafe impl < T : DynSend > DynSync for Lock < T > { }
214186}
215187
216- mod disabled {
217- use super :: Assume ;
188+ mod no_sync {
189+ use super :: Mode ;
218190 use std:: cell:: RefCell ;
219191
220192 pub use std:: cell:: RefMut as LockGuard ;
@@ -245,7 +217,7 @@ mod disabled {
245217 #[ inline( always) ]
246218 #[ track_caller]
247219 // This is unsafe to match the API for the `parallel_compiler` case.
248- pub unsafe fn lock_assume ( & self , _assume : Assume ) -> LockGuard < ' _ , T > {
220+ pub unsafe fn lock_assume ( & self , _mode : Mode ) -> LockGuard < ' _ , T > {
249221 self . 0 . borrow_mut ( )
250222 }
251223
0 commit comments