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,36 +64,26 @@ 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
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
+ }
81
78
}
82
79
}
83
80
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 > > ,
88
84
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 > ,
97
87
}
98
88
99
89
/// The value representing a locked state for the `Cell`.
@@ -102,23 +92,26 @@ mod enabled {
102
92
/// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
103
93
/// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`.
104
94
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 ,
106
101
data : UnsafeCell < T > ,
107
102
}
108
103
109
104
impl < T > Lock < T > {
110
105
#[ inline( always) ]
111
106
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) }
122
115
}
123
116
124
117
#[ inline( always) ]
@@ -131,79 +124,58 @@ mod enabled {
131
124
self . data . get_mut ( )
132
125
}
133
126
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.
136
146
///
137
147
/// 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 .
140
150
#[ inline( always) ]
141
151
#[ 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 > {
148
153
#[ inline( never) ]
149
154
#[ track_caller]
150
155
#[ cold]
151
156
fn lock_held ( ) -> ! {
152
157
panic ! ( "lock was already held" )
153
158
}
154
159
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
+ }
166
168
}
169
+ Mode :: Sync => self . mode_union . sync . lock ( ) ,
167
170
}
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
171
}
172
+ LockGuard { lock : self , marker : PhantomData , mode }
201
173
}
202
174
203
175
#[ inline( always) ]
204
176
#[ track_caller]
205
177
pub fn lock ( & self ) -> LockGuard < ' _ , T > {
206
- unsafe { self . lock_assume ( self . mode . to_assume ( ) ) }
178
+ unsafe { self . lock_assume ( self . mode ) }
207
179
}
208
180
}
209
181
@@ -213,8 +185,8 @@ mod enabled {
213
185
unsafe impl < T : DynSend > DynSync for Lock < T > { }
214
186
}
215
187
216
- mod disabled {
217
- use super :: Assume ;
188
+ mod no_sync {
189
+ use super :: Mode ;
218
190
use std:: cell:: RefCell ;
219
191
220
192
pub use std:: cell:: RefMut as LockGuard ;
@@ -245,7 +217,7 @@ mod disabled {
245
217
#[ inline( always) ]
246
218
#[ track_caller]
247
219
// 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 > {
249
221
self . 0 . borrow_mut ( )
250
222
}
251
223
0 commit comments