1+ use std:: marker:: PhantomData ;
2+ use std:: ops:: Deref ;
13use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
24use std:: sync:: { Arc , Condvar , Mutex } ;
35use std:: usize;
@@ -37,10 +39,15 @@ pub(super) trait Latch {
3739 ///
3840 /// Setting a latch triggers other threads to wake up and (in some
3941 /// 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,
4143 /// and it's typically better to read all the fields you will need
4244 /// 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 ) ;
4451}
4552
4653pub ( super ) trait AsCoreLatch {
@@ -123,8 +130,8 @@ impl CoreLatch {
123130 /// doing some wakeups; those are encapsulated in the surrounding
124131 /// latch code.
125132 #[ 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 ) ;
128135 old_state == SLEEPING
129136 }
130137
@@ -186,29 +193,29 @@ impl<'r> AsCoreLatch for SpinLatch<'r> {
186193
187194impl < ' r > Latch for SpinLatch < ' r > {
188195 #[ inline]
189- fn set ( & self ) {
196+ unsafe fn set ( this : * const Self ) {
190197 let cross_registry;
191198
192- let registry: & Registry = if self . cross {
199+ let registry: & Registry = if ( * this ) . cross {
193200 // Ensure the registry stays alive while we notify it.
194201 // Otherwise, it would be possible that we set the spin
195202 // latch and the other thread sees it and exits, causing
196203 // the registry to be deallocated, all before we get a
197204 // chance to invoke `registry.notify_worker_latch_is_set`.
198- cross_registry = Arc :: clone ( self . registry ) ;
205+ cross_registry = Arc :: clone ( ( * this ) . registry ) ;
199206 & cross_registry
200207 } else {
201208 // If this is not a "cross-registry" spin-latch, then the
202209 // thread which is performing `set` is itself ensuring
203210 // that the registry stays alive. However, that doesn't
204211 // include this *particular* `Arc` handle if the waiting
205212 // thread then exits, so we must completely dereference it.
206- self . registry
213+ ( * this ) . registry
207214 } ;
208- let target_worker_index = self . target_worker_index ;
215+ let target_worker_index = ( * this ) . target_worker_index ;
209216
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 ) {
212219 // Subtle: at this point, we can no longer read from
213220 // `self`, because the thread owning this spin latch may
214221 // have awoken and deallocated the latch. Therefore, we
@@ -255,10 +262,10 @@ impl LockLatch {
255262
256263impl Latch for LockLatch {
257264 #[ 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 ( ) ;
260267 * guard = true ;
261- self . v . notify_all ( ) ;
268+ ( * this ) . v . notify_all ( ) ;
262269 }
263270}
264271
@@ -307,9 +314,9 @@ impl CountLatch {
307314 /// count, then the latch is **set**, and calls to `probe()` will
308315 /// return true. Returns whether the latch was set.
309316 #[ 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 ) ;
313320 true
314321 } else {
315322 false
@@ -320,8 +327,12 @@ impl CountLatch {
320327 /// the latch is set, then the specific worker thread is tickled,
321328 /// which should be the one that owns this latch.
322329 #[ 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) {
325336 registry. notify_worker_latch_is_set ( target_worker_index) ;
326337 }
327338 }
@@ -362,19 +373,42 @@ impl CountLockLatch {
362373
363374impl Latch for CountLockLatch {
364375 #[ 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 ) ;
368379 }
369380 }
370381}
371382
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 > {
376410 #[ inline]
377- fn set ( & self ) {
378- L :: set ( self ) ;
411+ unsafe fn set ( this : * const Self ) {
412+ L :: set ( ( * this ) . inner ) ;
379413 }
380414}
0 commit comments