7
7
8
8
use std:: fmt;
9
9
10
- #[ cfg( not( parallel_compiler) ) ]
11
- pub use disabled:: * ;
12
10
#[ cfg( parallel_compiler) ]
13
- pub use enabled:: * ;
11
+ pub use maybe_sync:: * ;
12
+ #[ cfg( not( parallel_compiler) ) ]
13
+ pub use no_sync:: * ;
14
14
15
15
#[ derive( Clone , Copy , PartialEq ) ]
16
- pub enum Assume {
16
+ pub enum Mode {
17
17
NoSync ,
18
18
Sync ,
19
19
}
20
20
21
- mod enabled {
22
- use super :: Assume ;
21
+ mod maybe_sync {
22
+ use super :: Mode ;
23
23
use crate :: sync:: mode;
24
24
#[ cfg( parallel_compiler) ]
25
25
use crate :: sync:: { DynSend , DynSync } ;
26
26
use parking_lot:: lock_api:: RawMutex as _;
27
27
use parking_lot:: RawMutex ;
28
28
use std:: cell:: Cell ;
29
29
use std:: cell:: UnsafeCell ;
30
- use std:: hint:: unreachable_unchecked;
31
30
use std:: intrinsics:: unlikely;
32
31
use std:: marker:: PhantomData ;
32
+ use std:: mem:: ManuallyDrop ;
33
33
use std:: ops:: { Deref , DerefMut } ;
34
34
35
35
/// A guard holding mutable access to a `Lock` which is in a locked state.
@@ -40,7 +40,7 @@ mod enabled {
40
40
41
41
/// The syncronization mode of the lock. This is explicitly passed to let LLVM relate it
42
42
/// to the original lock operation.
43
- assume : Assume ,
43
+ mode : Mode ,
44
44
}
45
45
46
46
impl < ' a , T : ' a > Deref for LockGuard < ' a , T > {
@@ -64,12 +64,12 @@ mod enabled {
64
64
impl < ' a , T : ' a > Drop for LockGuard < ' a , T > {
65
65
#[ inline]
66
66
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
68
68
// with the lock state.
69
69
// SAFETY (unlock): We know that the lock is locked as this type is a proof of that.
70
70
unsafe {
71
71
self . lock . dispatch (
72
- self . assume ,
72
+ self . mode ,
73
73
|cell| {
74
74
debug_assert_eq ! ( cell. get( ) , true ) ;
75
75
cell. set ( false ) ;
@@ -81,19 +81,12 @@ mod enabled {
81
81
}
82
82
}
83
83
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 > > ,
88
87
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 > ,
97
90
}
98
91
99
92
/// The value representing a locked state for the `Cell`.
@@ -102,23 +95,26 @@ mod enabled {
102
95
/// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
103
96
/// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`.
104
97
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 ,
106
104
data : UnsafeCell < T > ,
107
105
}
108
106
109
107
impl < T > Lock < T > {
110
108
#[ inline( always) ]
111
109
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) }
122
118
}
123
119
124
120
#[ inline( always) ]
@@ -131,79 +127,58 @@ mod enabled {
131
127
self . data . get_mut ( )
132
128
}
133
129
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.
136
149
///
137
150
/// 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 .
140
153
#[ inline( always) ]
141
154
#[ 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 > {
148
156
#[ inline( never) ]
149
157
#[ track_caller]
150
158
#[ cold]
151
159
fn lock_held ( ) -> ! {
152
160
panic ! ( "lock was already held" )
153
161
}
154
162
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
+ }
166
171
}
172
+ Mode :: Sync => self . mode_union . lock . lock ( ) ,
167
173
}
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 }
200
174
}
175
+ LockGuard { lock : self , marker : PhantomData , mode }
201
176
}
202
177
203
178
#[ inline( always) ]
204
179
#[ track_caller]
205
180
pub fn lock ( & self ) -> LockGuard < ' _ , T > {
206
- unsafe { self . lock_assume ( self . mode . to_assume ( ) ) }
181
+ unsafe { self . lock_assume ( self . mode ) }
207
182
}
208
183
}
209
184
@@ -213,8 +188,8 @@ mod enabled {
213
188
unsafe impl < T : DynSend > DynSync for Lock < T > { }
214
189
}
215
190
216
- mod disabled {
217
- use super :: Assume ;
191
+ mod no_sync {
192
+ use super :: Mode ;
218
193
use std:: cell:: RefCell ;
219
194
220
195
pub use std:: cell:: RefMut as LockGuard ;
@@ -245,7 +220,7 @@ mod disabled {
245
220
#[ inline( always) ]
246
221
#[ track_caller]
247
222
// 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 > {
249
224
self . 0 . borrow_mut ( )
250
225
}
251
226
0 commit comments