use arc_swap::ArcSwapOption;
use async_task::Runnable;
use crossbeam_deque::{Injector, Steal, Stealer, Worker};
use crossbeam_utils::CachePadded;
use parking_lot::{Condvar, Mutex};
use std::cell::Cell;
use std::marker::PhantomData;
use std::ptr::NonNull;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering, fence};
use crate::cpu;
type StealerSlot = CachePadded<ArcSwapOption<Stealer<Runnable>>>;
const MAX_SHARDS: usize = 8;
const SPILL_THRESHOLD: u32 = 32;
thread_local! {
static SPILL: Cell<(usize, u32)> = const { Cell::new((usize::MAX, 0)) };
static CURRENT_WORKER: Cell<Option<WorkerHandle>> = const { Cell::new(None) };
}
#[derive(Clone, Copy)]
struct WorkerHandle {
queue: NonNull<Queue>,
deque: NonNull<Worker<Runnable>>,
}
pub(crate) struct Queue {
injectors: Box<[CachePadded<Injector<Runnable>>]>,
stealers: Box<[StealerSlot]>,
mask: usize,
park: Mutex<()>,
notify: Condvar,
parked: AtomicUsize,
shutdown: AtomicBool,
#[cfg(test)]
pub(crate) foreign_pushes: AtomicUsize,
}
pub(crate) struct WorkerContext {
idx: usize,
deque: Worker<Runnable>,
}
pub(crate) struct WorkerScope<'a> {
_phantom: PhantomData<&'a ()>,
}
impl Drop for WorkerScope<'_> {
fn drop(&mut self) {
CURRENT_WORKER.with(|w| w.set(None));
}
}
impl Queue {
pub(crate) fn new(num_workers: usize) -> Self {
let num_shards = num_workers.next_power_of_two().clamp(1, MAX_SHARDS);
let injectors: Vec<CachePadded<Injector<Runnable>>> =
(0..num_shards).map(|_| CachePadded::new(Injector::new())).collect();
let stealers: Vec<StealerSlot> =
(0..num_workers).map(|_| CachePadded::new(ArcSwapOption::empty())).collect();
Self {
injectors: injectors.into_boxed_slice(),
stealers: stealers.into_boxed_slice(),
mask: num_shards - 1,
park: Mutex::new(()),
notify: Condvar::new(),
parked: AtomicUsize::new(0),
shutdown: AtomicBool::new(false),
#[cfg(test)]
foreign_pushes: AtomicUsize::new(0),
}
}
pub(crate) fn register_worker(&self, idx: usize) -> WorkerContext {
let deque = Worker::new_fifo();
let stealer = deque.stealer();
self.stealers[idx].store(Some(Arc::new(stealer)));
WorkerContext {
idx,
deque,
}
}
pub(crate) fn enter_worker_scope<'a>(&'a self, ctx: &'a WorkerContext) -> WorkerScope<'a> {
CURRENT_WORKER.with(|w| {
w.set(Some(WorkerHandle {
queue: NonNull::from(self),
deque: NonNull::from(&ctx.deque),
}))
});
WorkerScope {
_phantom: PhantomData,
}
}
#[inline]
pub(crate) fn push(&self, runnable: Runnable) {
if let Some(handle) = CURRENT_WORKER.with(|w| w.get())
&& std::ptr::eq(handle.queue.as_ptr().cast_const(), self as *const Queue)
{
unsafe {
handle.deque.as_ref().push(runnable);
}
return;
}
#[cfg(test)]
self.foreign_pushes.fetch_add(1, Ordering::Relaxed);
if self.mask == 0 {
self.injectors[0].push(runnable);
} else {
let preferred = cpu::current_cpu() & self.mask;
let target = SPILL.with(|s| {
let (last, count) = s.get();
let new_count = if last == preferred {
count.saturating_add(1)
} else {
1
};
s.set((preferred, new_count));
if new_count <= SPILL_THRESHOLD {
preferred
} else {
(preferred + (new_count - SPILL_THRESHOLD) as usize) & self.mask
}
});
self.injectors[target].push(runnable);
}
fence(Ordering::SeqCst);
if self.parked.load(Ordering::Acquire) > 0 {
let _g = self.park.lock();
self.notify.notify_one();
}
}
#[inline]
pub(crate) fn pop_blocking(&self, ctx: &WorkerContext) -> Option<Runnable> {
loop {
if let Some(r) = self.scan(ctx) {
return Some(r);
}
let mut park = self.park.lock();
self.parked.fetch_add(1, Ordering::Release);
fence(Ordering::SeqCst);
if let Some(r) = self.scan(ctx) {
self.parked.fetch_sub(1, Ordering::Release);
return Some(r);
}
if self.shutdown.load(Ordering::Acquire) {
self.parked.fetch_sub(1, Ordering::Release);
return None;
}
self.notify.wait(&mut park);
self.parked.fetch_sub(1, Ordering::Release);
}
}
#[inline]
fn scan(&self, ctx: &WorkerContext) -> Option<Runnable> {
if let Some(r) = ctx.deque.pop() {
return Some(r);
}
let n = self.mask + 1;
let my_shard = ctx.idx & self.mask;
if let Some(r) = retry_steal(|| self.injectors[my_shard].steal_batch_and_pop(&ctx.deque)) {
return Some(r);
}
for offset in 1..n {
let idx = (my_shard + offset) & self.mask;
if let Some(r) = retry_steal(|| self.injectors[idx].steal_batch_and_pop(&ctx.deque)) {
return Some(r);
}
}
let num_workers = self.stealers.len();
for offset in 1..num_workers {
let victim = (ctx.idx + offset) % num_workers;
if let Some(stealer) = self.stealers[victim].load_full()
&& let Some(r) = retry_steal(|| stealer.steal_batch_and_pop(&ctx.deque))
{
return Some(r);
}
}
None
}
pub(crate) fn shutdown(&self) {
self.shutdown.store(true, Ordering::Release);
let _g = self.park.lock();
self.notify.notify_all();
}
}
#[inline]
fn retry_steal(mut f: impl FnMut() -> Steal<Runnable>) -> Option<Runnable> {
loop {
match f() {
Steal::Success(r) => return Some(r),
Steal::Empty => return None,
Steal::Retry => continue,
}
}
}