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,12 +64,12 @@ 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
67+ // SAFETY (dispatch): We get `self.mode ` from the lock operation so it is consistent
6868 // with the lock state.
6969 // SAFETY (unlock): We know that the lock is locked as this type is a proof of that.
7070 unsafe {
7171 self . lock . dispatch (
72- self . assume ,
72+ self . mode ,
7373 |cell| {
7474 debug_assert_eq ! ( cell. get( ) , true ) ;
7575 cell. set ( false ) ;
@@ -81,19 +81,12 @@ mod enabled {
8181 }
8282 }
8383
84- enum LockMode {
85- NoSync ( Cell < bool > ) ,
86- Sync ( RawMutex ) ,
87- }
84+ union ModeUnion {
85+ /// Indicates if the cell is locked. Only used if `Lock.mode` is `NoSync`.
86+ cell : ManuallyDrop < Cell < bool > > ,
8887
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- }
88+ /// A lock implementation that's only used if `Lock.mode` is `Sync`.
89+ lock : ManuallyDrop < RawMutex > ,
9790 }
9891
9992 /// The value representing a locked state for the `Cell`.
@@ -102,23 +95,26 @@ mod enabled {
10295 /// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
10396 /// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`.
10497 pub struct Lock < T > {
105- mode : LockMode ,
98+ /// Indicates if synchronization is used via `mode_union.lock` if `Sync`, or if a
99+ /// not thread safe cell is used via `mode_union.cell` if it's `NoSync`.
100+ /// This is set on initialization and never changed.
101+ mode : Mode ,
102+
103+ mode_union : ModeUnion ,
106104 data : UnsafeCell < T > ,
107105 }
108106
109107 impl < T > Lock < T > {
110108 #[ inline( always) ]
111109 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- }
110+ let ( mode, mode_union) = if unlikely ( mode:: might_be_dyn_thread_safe ( ) ) {
111+ // Create the lock with synchronization enabled using the `RawMutex` type.
112+ ( Mode :: Sync , ModeUnion { lock : ManuallyDrop :: new ( RawMutex :: INIT ) } )
113+ } else {
114+ // Create the lock with synchronization disabled.
115+ ( Mode :: NoSync , ModeUnion { cell : ManuallyDrop :: new ( Cell :: new ( !LOCKED ) ) } )
116+ } ;
117+ Lock { mode, mode_union, data : UnsafeCell :: new ( inner) }
122118 }
123119
124120 #[ inline( always) ]
@@ -131,79 +127,58 @@ mod enabled {
131127 self . data . get_mut ( )
132128 }
133129
134- /// This dispatches on the `LockMode` and gives access to its variants depending on
135- /// `assume`. If `no_sync` returns `None` this will panic.
130+ #[ inline( always) ]
131+ pub fn try_lock ( & self ) -> Option < LockGuard < ' _ , T > > {
132+ let mode = self . mode ;
133+ // SAFETY: This is safe since the union fields are used in accordance with `self.mode`.
134+ match mode {
135+ Mode :: NoSync => {
136+ let cell = unsafe { & self . mode_union . cell } ;
137+ let was_unlocked = cell. get ( ) != LOCKED ;
138+ if was_unlocked {
139+ cell. set ( LOCKED ) ;
140+ }
141+ was_unlocked
142+ }
143+ Mode :: Sync => unsafe { self . mode_union . lock . try_lock ( ) } ,
144+ }
145+ . then ( || LockGuard { lock : self , marker : PhantomData , mode } )
146+ }
147+
148+ /// This acquires the lock assuming syncronization is in a specific mode.
136149 ///
137150 /// Safety
138- /// This method must only be called if `might_be_dyn_thread_safe` on lock creation matches
139- /// matches the `assume` argument .
151+ /// This method must only be called with `Mode::Sync` if `might_be_dyn_thread_safe` was
152+ /// true on lock creation .
140153 #[ inline( always) ]
141154 #[ 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 {
155+ pub unsafe fn lock_assume ( & self , mode : Mode ) -> LockGuard < ' _ , T > {
148156 #[ inline( never) ]
149157 #[ track_caller]
150158 #[ cold]
151159 fn lock_held ( ) -> ! {
152160 panic ! ( "lock was already held" )
153161 }
154162
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 ( )
163+ // SAFETY: This is safe since the union fields are used in accordance with `mode`
164+ // which also must match `self.mode` due to the safety precondition.
165+ unsafe {
166+ match mode {
167+ Mode :: NoSync => {
168+ if unlikely ( self . mode_union . cell . replace ( LOCKED ) == LOCKED ) {
169+ lock_held ( )
170+ }
166171 }
172+ Mode :: Sync => self . mode_union . lock . lock ( ) ,
167173 }
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 }
200174 }
175+ LockGuard { lock : self , marker : PhantomData , mode }
201176 }
202177
203178 #[ inline( always) ]
204179 #[ track_caller]
205180 pub fn lock ( & self ) -> LockGuard < ' _ , T > {
206- unsafe { self . lock_assume ( self . mode . to_assume ( ) ) }
181+ unsafe { self . lock_assume ( self . mode ) }
207182 }
208183 }
209184
@@ -213,8 +188,8 @@ mod enabled {
213188 unsafe impl < T : DynSend > DynSync for Lock < T > { }
214189}
215190
216- mod disabled {
217- use super :: Assume ;
191+ mod no_sync {
192+ use super :: Mode ;
218193 use std:: cell:: RefCell ;
219194
220195 pub use std:: cell:: RefMut as LockGuard ;
@@ -245,7 +220,7 @@ mod disabled {
245220 #[ inline( always) ]
246221 #[ track_caller]
247222 // This is unsafe to match the API for the `parallel_compiler` case.
248- pub unsafe fn lock_assume ( & self , _assume : Assume ) -> LockGuard < ' _ , T > {
223+ pub unsafe fn lock_assume ( & self , _mode : Mode ) -> LockGuard < ' _ , T > {
249224 self . 0 . borrow_mut ( )
250225 }
251226
0 commit comments