1
+ use std:: marker:: PhantomData ;
2
+ use std:: ops:: Deref ;
1
3
use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
2
4
use std:: sync:: { Arc , Condvar , Mutex } ;
3
5
use std:: usize;
@@ -37,10 +39,15 @@ pub(super) trait Latch {
37
39
///
38
40
/// Setting a latch triggers other threads to wake up and (in some
39
41
/// cases) complete. This may, in turn, cause memory to be
40
- /// allocated and so forth. One must be very careful about this,
42
+ /// deallocated and so forth. One must be very careful about this,
41
43
/// and it's typically better to read all the fields you will need
42
44
/// to access *before* a latch is set!
43
- fn set ( & self ) ;
45
+ ///
46
+ /// This function operates on `*const Self` instead of `&self` to allow it
47
+ /// to become dangling during this call. The caller must ensure that the
48
+ /// pointer is valid upon entry, and not invalidated during the call by any
49
+ /// actions other than `set` itself.
50
+ unsafe fn set ( this : * const Self ) ;
44
51
}
45
52
46
53
pub ( super ) trait AsCoreLatch {
@@ -123,8 +130,8 @@ impl CoreLatch {
123
130
/// doing some wakeups; those are encapsulated in the surrounding
124
131
/// latch code.
125
132
#[ inline]
126
- fn set ( & self ) -> bool {
127
- let old_state = self . state . swap ( SET , Ordering :: AcqRel ) ;
133
+ unsafe fn set ( this : * const Self ) -> bool {
134
+ let old_state = ( * this ) . state . swap ( SET , Ordering :: AcqRel ) ;
128
135
old_state == SLEEPING
129
136
}
130
137
@@ -186,29 +193,29 @@ impl<'r> AsCoreLatch for SpinLatch<'r> {
186
193
187
194
impl < ' r > Latch for SpinLatch < ' r > {
188
195
#[ inline]
189
- fn set ( & self ) {
196
+ unsafe fn set ( this : * const Self ) {
190
197
let cross_registry;
191
198
192
- let registry: & Registry = if self . cross {
199
+ let registry: & Registry = if ( * this ) . cross {
193
200
// Ensure the registry stays alive while we notify it.
194
201
// Otherwise, it would be possible that we set the spin
195
202
// latch and the other thread sees it and exits, causing
196
203
// the registry to be deallocated, all before we get a
197
204
// chance to invoke `registry.notify_worker_latch_is_set`.
198
- cross_registry = Arc :: clone ( self . registry ) ;
205
+ cross_registry = Arc :: clone ( ( * this ) . registry ) ;
199
206
& cross_registry
200
207
} else {
201
208
// If this is not a "cross-registry" spin-latch, then the
202
209
// thread which is performing `set` is itself ensuring
203
210
// that the registry stays alive. However, that doesn't
204
211
// include this *particular* `Arc` handle if the waiting
205
212
// thread then exits, so we must completely dereference it.
206
- self . registry
213
+ ( * this ) . registry
207
214
} ;
208
- let target_worker_index = self . target_worker_index ;
215
+ let target_worker_index = ( * this ) . target_worker_index ;
209
216
210
- // NOTE: Once we `set`, the target may proceed and invalidate `&self `!
211
- if self . core_latch . set ( ) {
217
+ // NOTE: Once we `set`, the target may proceed and invalidate `this `!
218
+ if CoreLatch :: set ( & ( * this ) . core_latch ) {
212
219
// Subtle: at this point, we can no longer read from
213
220
// `self`, because the thread owning this spin latch may
214
221
// have awoken and deallocated the latch. Therefore, we
@@ -255,10 +262,10 @@ impl LockLatch {
255
262
256
263
impl Latch for LockLatch {
257
264
#[ inline]
258
- fn set ( & self ) {
259
- let mut guard = self . m . lock ( ) . unwrap ( ) ;
265
+ unsafe fn set ( this : * const Self ) {
266
+ let mut guard = ( * this ) . m . lock ( ) . unwrap ( ) ;
260
267
* guard = true ;
261
- self . v . notify_all ( ) ;
268
+ ( * this ) . v . notify_all ( ) ;
262
269
}
263
270
}
264
271
@@ -307,9 +314,9 @@ impl CountLatch {
307
314
/// count, then the latch is **set**, and calls to `probe()` will
308
315
/// return true. Returns whether the latch was set.
309
316
#[ inline]
310
- pub ( super ) fn set ( & self ) -> bool {
311
- if self . counter . fetch_sub ( 1 , Ordering :: SeqCst ) == 1 {
312
- self . core_latch . set ( ) ;
317
+ pub ( super ) unsafe fn set ( this : * const Self ) -> bool {
318
+ if ( * this ) . counter . fetch_sub ( 1 , Ordering :: SeqCst ) == 1 {
319
+ CoreLatch :: set ( & ( * this ) . core_latch ) ;
313
320
true
314
321
} else {
315
322
false
@@ -320,8 +327,12 @@ impl CountLatch {
320
327
/// the latch is set, then the specific worker thread is tickled,
321
328
/// which should be the one that owns this latch.
322
329
#[ inline]
323
- pub ( super ) fn set_and_tickle_one ( & self , registry : & Registry , target_worker_index : usize ) {
324
- if self . set ( ) {
330
+ pub ( super ) unsafe fn set_and_tickle_one (
331
+ this : * const Self ,
332
+ registry : & Registry ,
333
+ target_worker_index : usize ,
334
+ ) {
335
+ if Self :: set ( this) {
325
336
registry. notify_worker_latch_is_set ( target_worker_index) ;
326
337
}
327
338
}
@@ -362,19 +373,42 @@ impl CountLockLatch {
362
373
363
374
impl Latch for CountLockLatch {
364
375
#[ inline]
365
- fn set ( & self ) {
366
- if self . counter . fetch_sub ( 1 , Ordering :: SeqCst ) == 1 {
367
- self . lock_latch . set ( ) ;
376
+ unsafe fn set ( this : * const Self ) {
377
+ if ( * this ) . counter . fetch_sub ( 1 , Ordering :: SeqCst ) == 1 {
378
+ LockLatch :: set ( & ( * this ) . lock_latch ) ;
368
379
}
369
380
}
370
381
}
371
382
372
- impl < ' a , L > Latch for & ' a L
373
- where
374
- L : Latch ,
375
- {
383
+ /// `&L` without any implication of `dereferenceable` for `Latch::set`
384
+ pub ( super ) struct LatchRef < ' a , L > {
385
+ inner : * const L ,
386
+ marker : PhantomData < & ' a L > ,
387
+ }
388
+
389
+ impl < L > LatchRef < ' _ , L > {
390
+ pub ( super ) fn new ( inner : & L ) -> LatchRef < ' _ , L > {
391
+ LatchRef {
392
+ inner,
393
+ marker : PhantomData ,
394
+ }
395
+ }
396
+ }
397
+
398
+ unsafe impl < L : Sync > Sync for LatchRef < ' _ , L > { }
399
+
400
+ impl < L > Deref for LatchRef < ' _ , L > {
401
+ type Target = L ;
402
+
403
+ fn deref ( & self ) -> & L {
404
+ // SAFETY: if we have &self, the inner latch is still alive
405
+ unsafe { & * self . inner }
406
+ }
407
+ }
408
+
409
+ impl < L : Latch > Latch for LatchRef < ' _ , L > {
376
410
#[ inline]
377
- fn set ( & self ) {
378
- L :: set ( self ) ;
411
+ unsafe fn set ( this : * const Self ) {
412
+ L :: set ( ( * this ) . inner ) ;
379
413
}
380
414
}
0 commit comments