@@ -142,6 +142,13 @@ impl CoreLatch {
142
142
}
143
143
}
144
144
145
+ impl AsCoreLatch for CoreLatch {
146
+ #[ inline]
147
+ fn as_core_latch ( & self ) -> & CoreLatch {
148
+ self
149
+ }
150
+ }
151
+
145
152
/// Spin latches are the simplest, most efficient kind, but they do
146
153
/// not support a `wait()` operation. They just have a boolean flag
147
154
/// that becomes true when `set()` is called.
@@ -269,96 +276,114 @@ impl Latch for LockLatch {
269
276
}
270
277
}
271
278
272
- /// Counting latches are used to implement scopes. They track a
273
- /// counter. Unlike other latches, calling `set()` does not
274
- /// necessarily make the latch be considered `set()`; instead, it just
275
- /// decrements the counter. The latch is only "set" (in the sense that
276
- /// `probe()` returns true) once the counter reaches zero.
279
+ /// Once latches are used to implement one-time blocking, primarily
280
+ /// for the termination flag of the threads in the pool.
277
281
///
278
- /// Note: like a `SpinLatch`, count laches are always associated with
282
+ /// Note: like a `SpinLatch`, once-latches are always associated with
279
283
/// some registry that is probing them, which must be tickled when
280
284
/// they are set. *Unlike* a `SpinLatch`, they don't themselves hold a
281
285
/// reference to that registry. This is because in some cases the
282
- /// registry owns the count -latch, and that would create a cycle. So a
283
- /// `CountLatch ` must be given a reference to its owning registry when
286
+ /// registry owns the once -latch, and that would create a cycle. So a
287
+ /// `OnceLatch ` must be given a reference to its owning registry when
284
288
/// it is set. For this reason, it does not implement the `Latch`
285
289
/// trait (but it doesn't have to, as it is not used in those generic
286
290
/// contexts).
287
291
#[ derive( Debug ) ]
288
- pub ( super ) struct CountLatch {
289
- // counter is first to nudge layout like CountLockLatch
290
- counter : AtomicUsize ,
292
+ pub ( super ) struct OnceLatch {
291
293
core_latch : CoreLatch ,
292
294
}
293
295
294
- impl CountLatch {
295
- #[ inline]
296
- pub ( super ) fn new ( ) -> CountLatch {
297
- Self :: with_count ( 1 )
298
- }
299
-
296
+ impl OnceLatch {
300
297
#[ inline]
301
- pub ( super ) fn with_count ( n : usize ) -> CountLatch {
302
- CountLatch {
298
+ pub ( super ) fn new ( ) -> OnceLatch {
299
+ Self {
303
300
core_latch : CoreLatch :: new ( ) ,
304
- counter : AtomicUsize :: new ( n) ,
305
301
}
306
302
}
307
303
308
- #[ inline]
309
- pub ( super ) fn increment ( & self ) {
310
- debug_assert ! ( !self . core_latch. probe( ) ) ;
311
- self . counter . fetch_add ( 1 , Ordering :: Relaxed ) ;
312
- }
313
-
314
- /// Decrements the latch counter by one. If this is the final
315
- /// count, then the latch is **set**, and calls to `probe()` will
316
- /// return true. Returns whether the latch was set.
317
- #[ inline]
318
- pub ( super ) unsafe fn set ( this : * const Self ) -> bool {
319
- if ( * this) . counter . fetch_sub ( 1 , Ordering :: SeqCst ) == 1 {
320
- CoreLatch :: set ( & ( * this) . core_latch ) ;
321
- true
322
- } else {
323
- false
324
- }
325
- }
326
-
327
- /// Decrements the latch counter by one and possibly set it. If
328
- /// the latch is set, then the specific worker thread is tickled,
304
+ /// Set the latch, then tickle the specific worker thread,
329
305
/// which should be the one that owns this latch.
330
306
#[ inline]
331
307
pub ( super ) unsafe fn set_and_tickle_one (
332
308
this : * const Self ,
333
309
registry : & Registry ,
334
310
target_worker_index : usize ,
335
311
) {
336
- if Self :: set ( this) {
312
+ if CoreLatch :: set ( & ( * this) . core_latch ) {
337
313
registry. notify_worker_latch_is_set ( target_worker_index) ;
338
314
}
339
315
}
340
316
}
341
317
342
- impl AsCoreLatch for CountLatch {
318
+ impl AsCoreLatch for OnceLatch {
343
319
#[ inline]
344
320
fn as_core_latch ( & self ) -> & CoreLatch {
345
321
& self . core_latch
346
322
}
347
323
}
348
324
325
+ /// Counting latches are used to implement scopes. They track a
326
+ /// counter. Unlike other latches, calling `set()` does not
327
+ /// necessarily make the latch be considered `set()`; instead, it just
328
+ /// decrements the counter. The latch is only "set" (in the sense that
329
+ /// `probe()` returns true) once the counter reaches zero.
349
330
#[ derive( Debug ) ]
350
- pub ( super ) struct CountLockLatch {
351
- // counter is first to nudge layout like CountLatch
331
+ pub ( super ) struct CountLatch {
352
332
counter : AtomicUsize ,
353
- lock_latch : LockLatch ,
333
+ kind : CountLatchKind ,
354
334
}
355
335
356
- impl CountLockLatch {
357
- #[ inline]
358
- pub ( super ) fn with_count ( n : usize ) -> CountLockLatch {
359
- CountLockLatch {
360
- lock_latch : LockLatch :: new ( ) ,
361
- counter : AtomicUsize :: new ( n) ,
336
+ enum CountLatchKind {
337
+ /// A latch for scopes created on a rayon thread which will participate in work-
338
+ /// stealing while it waits for completion. This thread is not necessarily part
339
+ /// of the same registry as the scope itself!
340
+ Stealing {
341
+ latch : CoreLatch ,
342
+ /// If a worker thread in registry A calls `in_place_scope` on a ThreadPool
343
+ /// with registry B, when a job completes in a thread of registry B, we may
344
+ /// need to call `notify_worker_latch_is_set()` to wake the thread in registry A.
345
+ /// That means we need a reference to registry A (since at that point we will
346
+ /// only have a reference to registry B), so we stash it here.
347
+ registry : Arc < Registry > ,
348
+ /// The index of the worker to wake in `registry`
349
+ worker_index : usize ,
350
+ } ,
351
+
352
+ /// A latch for scopes created on a non-rayon thread which will block to wait.
353
+ Blocking { latch : LockLatch } ,
354
+ }
355
+
356
+ impl std:: fmt:: Debug for CountLatchKind {
357
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
358
+ match self {
359
+ CountLatchKind :: Stealing { latch, .. } => {
360
+ f. debug_tuple ( "Stealing" ) . field ( latch) . finish ( )
361
+ }
362
+ CountLatchKind :: Blocking { latch, .. } => {
363
+ f. debug_tuple ( "Blocking" ) . field ( latch) . finish ( )
364
+ }
365
+ }
366
+ }
367
+ }
368
+
369
+ impl CountLatch {
370
+ pub ( super ) fn new ( owner : Option < & WorkerThread > ) -> Self {
371
+ Self :: with_count ( 1 , owner)
372
+ }
373
+
374
+ pub ( super ) fn with_count ( count : usize , owner : Option < & WorkerThread > ) -> Self {
375
+ Self {
376
+ counter : AtomicUsize :: new ( count) ,
377
+ kind : match owner {
378
+ Some ( owner) => CountLatchKind :: Stealing {
379
+ latch : CoreLatch :: new ( ) ,
380
+ registry : Arc :: clone ( owner. registry ( ) ) ,
381
+ worker_index : owner. index ( ) ,
382
+ } ,
383
+ None => CountLatchKind :: Blocking {
384
+ latch : LockLatch :: new ( ) ,
385
+ } ,
386
+ } ,
362
387
}
363
388
}
364
389
@@ -368,16 +393,42 @@ impl CountLockLatch {
368
393
debug_assert ! ( old_counter != 0 ) ;
369
394
}
370
395
371
- pub ( super ) fn wait ( & self ) {
372
- self . lock_latch . wait ( ) ;
396
+ pub ( super ) fn wait ( & self , owner : Option < & WorkerThread > ) {
397
+ match & self . kind {
398
+ CountLatchKind :: Stealing {
399
+ latch,
400
+ registry,
401
+ worker_index,
402
+ } => unsafe {
403
+ let owner = owner. expect ( "owner thread" ) ;
404
+ debug_assert_eq ! ( registry. id( ) , owner. registry( ) . id( ) ) ;
405
+ debug_assert_eq ! ( * worker_index, owner. index( ) ) ;
406
+ owner. wait_until ( latch) ;
407
+ } ,
408
+ CountLatchKind :: Blocking { latch } => latch. wait ( ) ,
409
+ }
373
410
}
374
411
}
375
412
376
- impl Latch for CountLockLatch {
413
+ impl Latch for CountLatch {
377
414
#[ inline]
378
415
unsafe fn set ( this : * const Self ) {
379
416
if ( * this) . counter . fetch_sub ( 1 , Ordering :: SeqCst ) == 1 {
380
- LockLatch :: set ( & ( * this) . lock_latch ) ;
417
+ // NOTE: Once we call `set` on the internal `latch`,
418
+ // the target may proceed and invalidate `this`!
419
+ match ( * this) . kind {
420
+ CountLatchKind :: Stealing {
421
+ ref latch,
422
+ ref registry,
423
+ worker_index,
424
+ } => {
425
+ let registry = Arc :: clone ( registry) ;
426
+ if CoreLatch :: set ( latch) {
427
+ registry. notify_worker_latch_is_set ( worker_index) ;
428
+ }
429
+ }
430
+ CountLatchKind :: Blocking { ref latch } => LockLatch :: set ( latch) ,
431
+ }
381
432
}
382
433
}
383
434
}
0 commit comments