use alloc::boxed::Box;
use core::ops::ControlFlow;
use core::sync::atomic::{AtomicIsize, Ordering};
use crate::config::Config;
use crate::control::{AtomicControl, WakerSlot};
use crate::pubqueue::{PQContents, Priority, PubQueue, Stealer};
use crate::task::Task;
use crate::util::CachePadded;
pub struct Queue<T: Task> {
pub(crate) config: Config,
pub(crate) queue_control: Box<CachePadded<AtomicQueueControl>>,
pub(crate) priority: Box<[CachePadded<Priority<T>>]>,
pub(crate) stealer: Box<[CachePadded<Stealer>]>,
pub(crate) pq_contents: Box<[Box<PQContents<T>>]>,
pub(crate) control: Box<[CachePadded<AtomicControl>]>,
pub(crate) waker_slot: Box<[CachePadded<WakerSlot>]>,
}
impl<T: Task> Queue<T> {
pub fn new(config: Config) -> Self {
let num_workers = config.num_workers.get();
let queue_control =
Box::new(CachePadded::new(AtomicQueueControl::new(num_workers)));
let priority = (0..num_workers)
.map(|_| CachePadded::new(Priority::default()))
.collect();
let stealer = (0..num_workers)
.map(|id| CachePadded::new(unsafe { Stealer::new(id, &config) }))
.collect();
let pq_contents = (0..num_workers)
.map(|_| PQContents::new_boxed(&config))
.collect();
let control = (0..num_workers)
.map(|_| CachePadded::new(AtomicControl::new()))
.collect();
let waker_slot = (0..num_workers)
.map(|_| CachePadded::new(WakerSlot::new()))
.collect();
Self {
config,
queue_control,
priority,
stealer,
pq_contents,
control,
waker_slot,
}
}
#[inline]
pub const fn config(&self) -> &Config {
&self.config
}
pub fn shutdown(&self) {
self.queue_control.shutdown();
for (control, waker_slot) in self.control.iter().zip(&self.waker_slot) {
unsafe { control.try_wake(waker_slot) };
}
}
pub fn has_shut_down(&self) -> bool {
self.queue_control.has_shut_down()
}
pub(crate) unsafe fn steal(
&self,
min_priority: Option<T::Priority>,
replacement: usize,
) -> Option<PubQueue> {
debug_assert!(replacement < self.config.num_workers.get());
let num_workers = self.config.num_workers.get();
let index = (0..num_workers).find(|&index| unsafe {
self.priority[index].steal_if_above(min_priority).is_some()
})?;
Some(unsafe { self.stealer[index].steal(replacement, &self.config) })
}
pub(crate) fn wake(&self) {
for (control, waker_slot) in self.control.iter().zip(&self.waker_slot) {
if unsafe { control.try_wake(waker_slot) } {
break;
}
}
}
}
#[repr(transparent)]
pub struct AtomicQueueControl {
raw: AtomicIsize,
}
impl AtomicQueueControl {
pub const fn new(num_workers: usize) -> Self {
assert!(num_workers <= isize::MAX as usize);
let num_workers = num_workers as isize;
Self {
raw: AtomicIsize::new(num_workers),
}
}
pub fn mark_asleep(&self) -> ControlFlow<(), usize> {
match self.raw.fetch_update(
Ordering::Release,
Ordering::Relaxed,
|state| (state >= 0).then(|| state.strict_sub(1)),
) {
Ok(state) => ControlFlow::Continue(state as usize - 1),
Err(_) => ControlFlow::Break(()),
}
}
pub fn mark_awake(&self) -> ControlFlow<()> {
match self.raw.fetch_update(
Ordering::Release,
Ordering::Relaxed,
|state| (state >= 0).then(|| state.strict_add(1)),
) {
Ok(_) => ControlFlow::Continue(()),
Err(_) => ControlFlow::Break(()),
}
}
pub fn shutdown(&self) {
self.raw.store(-1, Ordering::Relaxed);
}
pub fn has_shut_down(&self) -> bool {
self.raw.load(Ordering::Relaxed) < 0
}
}