1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
//! Sharded MPMC queue used by `Threadpool` to deliver `Runnable`s from
//! producers to worker threads. Backed by `crossbeam_deque` —
//! `Injector`s for cross-thread handoff and per-worker `Worker` deques
//! for the steady-state hot path.
//!
//! ## Architecture
//!
//! Three pools of storage:
//!
//! * **Sharded injectors.** Up to [`MAX_SHARDS`] lock-free MPMC
//! `Injector<Runnable>`s. Producers pick a shard via a cached hash
//! of their thread ID (see [`crate::cpu`]) — a given producer
//! consistently lands on the same shard. The count defaults to
//! `num_workers.next_power_of_two().min(MAX_SHARDS)` and can be
//! overridden per pool by [`crate::Builder::shards`]; it is always a
//! power of two so the routing is a bitmask, and a single-worker pool
//! degenerates to one shard with no scan cost.
//! * **Per-worker deques.** Each worker owns one
//! `crossbeam_deque::Worker<Runnable>`. The owner thread is the
//! only producer to its own deque; pop/push from the owner is
//! lock-free and uncontended. The fast path is: worker steals a
//! *batch* from its preferred injector into its own deque, then
//! drains the local deque without crossing any shared state until
//! it empties.
//! * **Stealers.** A `Stealer<Runnable>` for each worker's deque is
//! stored centrally so workers can steal from each other as a
//! last resort before parking. Held in `ArcSwapOption<Stealer>`
//! slots: the cross-worker scan path reads lock-free with a
//! single atomic load, and the panic-respawn path atomically
//! replaces a worker's slot when [`Sentry`] starts a fresh
//! thread.
//!
//! Pop order: own deque → preferred injector → other injectors →
//! other workers' stealers → bounded spin → park.
//!
//! The two cross-worker steps visit every victim exactly once but
//! start at a per-worker random rotation, so idle workers don't
//! convoy onto the same injector in lockstep. On the unarmed scans
//! each victim is `is_empty()`-probed before the steal is attempted,
//! turning an idle pool's repeated scanning into shared loads rather
//! than a storm of contended CAS. A `Steal::Retry` moves on to the
//! next victim instead of spinning on the contended one.
//!
//! Before parking, a worker re-scans a few times with `spin_loop`
//! backoff ([`SPIN_ROUNDS`]) so a runnable already on its way is
//! caught without a futex round-trip, then a few more with
//! `yield_now` ([`YIELD_ROUNDS`]) so an oversubscribed pool hands the
//! CPU back to the producer instead of spinning against it.
//!
//! This phase applies to every pool size. An earlier revision skipped it
//! for one- and two-worker pools, on the theory that a small pool has no
//! contention to amortise — measurement on an idle 32-core Linux box
//! says the opposite: ungated, a single-worker submit-and-await round
//! trip drops from 4.4 us to 1.1 us, because the worker is still
//! spinning when the task lands and never takes the futex round trip at
//! all. It costs 15-20% on `multi_producer` with one worker and many
//! producers, where the worker is saturated and the backoff is pure
//! delay. Note that a CPU-*contended* machine inverts this trade, since
//! a spinning worker there competes with the producer it is waiting
//! for.
//!
//! ## Producer spill
//!
//! Per-producer shard routing is a win when multiple producers are
//! active (the `multi_producer` benches): each producer hashes to its
//! own shard, so their traffic stays isolated. For a single-producer
//! `current_thread` runtime, every push hashes to the *same* shard and
//! the other N-1 workers idle-scan. To defeat that, producers track a
//! thread-local `(last_shard, count)`: after [`SPILL_THRESHOLD`]
//! consecutive pushes to the same preferred shard, subsequent pushes
//! rotate to neighbouring shards. Multi-producer workloads rarely trip
//! the threshold (their pushes interleave across distinct shards) and
//! stay fully affine.
//!
//! ## Lock-ordering and the parked-handshake
//!
//! Producers push to a shard's injector, then check the `parked`
//! atomic. If any worker may be parked, the producer briefly takes
//! the `park` mutex to call `notify_one`. Workers, when parking,
//! acquire `park` first, bump `parked` *before* a final re-scan of
//! all shards and stealers, then `cv.wait` (which atomically
//! releases `park`).
//!
//! Unlike the previous mutex-shard design, the cross-thread
//! happens-before edge no longer flows through a shard mutex.
//! [`crossbeam_deque::Injector`] is lock-free; pushes and steals
//! synchronise through the injector's internal atomics, but those
//! orderings alone aren't enough to close the producer↔worker
//! race on `parked`. The queue therefore inserts a
//! [`fence(SeqCst)`] between each side's queue access and its
//! `parked` access — the textbook Dekker pattern.
//!
//! [`fence(SeqCst)`]: std::sync::atomic::fence
//!
//! **Proof sketch (Dekker fence pattern).** With the fences in
//! place, the producer's `Injector::push` is sequenced-before its
//! `fence(SeqCst)`, which is sequenced-before its
//! `parked.load`; the worker's `parked.fetch_add` is
//! sequenced-before its `fence(SeqCst)`, which is sequenced-before
//! its `Injector::steal`. Both `fence(SeqCst)`s appear in a single
//! SeqCst total order.
//!
//! Assume for contradiction that a wakeup is lost — i.e., the
//! producer's `parked.load` reads 0 (so producer takes the fast
//! path and skips `notify_one`) AND the worker's `Injector::steal`
//! finds nothing (so the worker proceeds into `cv.wait`).
//!
//! * `parked.load = 0` means the worker's `parked.fetch_add` is
//! *after* the producer's `parked.load` in `parked`'s
//! modification order. By the SeqCst fence rule, the worker's
//! fence is then after the producer's fence in SeqCst order.
//! * `Injector::steal = empty` means the producer's `Injector::push`
//! is *after* the worker's `Injector::steal` in the injector's
//! modification order. By the SeqCst fence rule, the producer's
//! fence is then after the worker's fence in SeqCst order.
//!
//! These two conclusions contradict — the fences can't both be
//! before each other in the SeqCst total order. So at least one
//! of (`parked.load = 0`) or (`Injector::steal = empty`) is false,
//! and the wakeup is delivered.
//!
//! Either way: if the worker's re-scan finds the runnable, the
//! worker doesn't park. If the producer's notify path runs, it
//! synchronises through `park.lock()` — which blocks until the
//! worker is already in `cv.wait` (since the worker holds `park`
//! across arm + re-scan and `cv.wait` atomically releases it).
//!
//! [`Sentry`]: crate::sentry::Sentry
use ArcSwapOption;
use Runnable;
use ;
use ;
use ;
use Cell;
use PhantomData;
use NonNull;
use Arc;
use ;
use cratecpu;
/// Per-worker stealer slot. `ArcSwapOption` so the steal-scan path
/// can read with a single atomic load (no mutex acquire), while
/// the panic-respawn path can still atomically replace a worker's
/// slot when a fresh thread takes over. `CachePadded` to keep slots
/// on separate cache lines and avoid false sharing during scans.
type StealerSlot = ;
/// Default cap on the shard count, overridable per pool via
/// [`crate::Builder::shards`]. Picked empirically: 8 saturates
/// producer-side distribution on common topologies (≤8-core boxes
/// map one shard per core, 32-core boxes share 4 cores per shard)
/// while keeping the worst-case empty scan at 8 cheap loads.
const MAX_SHARDS: usize = 8;
/// Consecutive pushes to the same preferred shard before producer-
/// side spill kicks in. Multi-producer workloads rarely reach it
/// (their pushes interleave, resetting the counter), while a
/// single-producer fan-out trips it quickly so the work spreads
/// across shards before the other workers give up and park.
const SPILL_THRESHOLD: u32 = 8;
/// Victims a *cheap* scan pass inspects before giving up, per step.
///
/// The armed re-scan still visits every injector and every peer — that
/// sweep is load-bearing for the lost-wakeup proof — but the unarmed and
/// pre-park passes stop here, because their cost is paid on every wake
/// and it grows with the pool. Unbounded, a single submit-and-await round
/// trip on a 512-worker pool costs ~69 µs against ~2 µs at 8 workers,
/// almost all of it spent walking idle peers. The per-worker random start
/// rotates which victims each pass inspects, so a few passes still cover
/// a wide spread.
///
/// Bounding this cannot strand work, for three independent reasons, which
/// is why the cap can be this aggressive: a worker always checks its
/// preferred injector unbounded, so every shard has a dedicated checker
/// whenever the shard count is at most the worker count; where it is not
/// (the count is rounded up to a power of two), reaching more than eight
/// others requires at least nine workers, all scanning with independent
/// random rotations; and the sweep a worker performs immediately before
/// parking is exhaustive regardless. That last one is what the
/// lost-wakeup proof actually rests on — it is a visibility-ordering
/// requirement, not a coverage one, which is why it must stay unbounded
/// even though liveness would survive without it.
const CHEAP_SCAN_VICTIMS: usize = 8;
/// Re-scans performed with `spin_loop` backoff before a worker gives
/// up its CPU. The point is to catch a runnable already on its way, not
/// to poll — spinning is the cheapest way to win a submit-and-await
/// round trip, but it burns a core.
///
/// Six matches `crossbeam_utils::Backoff`'s own spin/yield boundary
/// (`SPIN_LIMIT`), i.e. exactly the point at which that crate stops
/// spinning and starts yielding, so the two halves of this phase line up
/// with the backoff primitive driving them. That is a principled anchor
/// rather than a measured optimum — see the caveat in the module docs
/// about tuning these against a busy machine.
const SPIN_ROUNDS: u32 = 6;
/// Re-scans performed with `yield_now` between them, after
/// [`SPIN_ROUNDS`] and before parking. These exist for the
/// *oversubscribed* case: when workers outnumber free cores, an idle
/// worker that only spins starves the very producer it is waiting
/// for, so handing the CPU back beats both spinning and an immediate
/// park. Kept small too — each yield is a syscall.
const YIELD_ROUNDS: u32 = 4;
thread_local!
/// Pointer pair stashed in [`CURRENT_WORKER`] for the duration of
/// a worker thread's scope. Both pointers are valid only while
/// the corresponding [`WorkerScope`] is alive.
// `WorkerHandle` holds raw pointers and is only ever read on the
// thread that wrote it (the worker thread itself), so `Send` /
// `Sync` aren't needed and aren't requested. The thread-local
// machinery handles per-thread isolation.
/// Shared work queue. `push` notifies one waiter; `pop_blocking`
/// drains local + steals from shards + steals from peers, then
/// parks until a runnable arrives or shutdown is signalled.
pub
/// Per-worker state owned exclusively by one worker OS thread. The
/// `Worker<Runnable>` deque is `!Sync`; constructed inside the
/// worker closure via [`Queue::register_worker`] so panic-respawn
/// gets a fresh deque on each start.
pub
/// How hard a [`Queue::scan`] pass should work to notice a runnable.
/// See [`Queue::scan`] for why the armed re-scan must stay strict.
/// Advance an xorshift32 and return the new state. Cheap enough
/// (3 shifts, 3 xors) to run on every scan pass.
/// RAII guard returned by [`Queue::enter_worker_scope`]. While
/// alive, the calling thread's [`CURRENT_WORKER`] holds a handle
/// to the queue + the worker's local deque so [`Queue::push`]
/// from the same thread can fast-path. On drop, clears the
/// thread-local so any later `push` from this thread falls back
/// to the foreign-producer path.
pub