use core::{
cell::UnsafeCell,
hint::unreachable_unchecked,
ops::ControlFlow,
sync::atomic::{AtomicU32, Ordering},
task::Waker,
};
use crate::util::Backoff;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Control {
Initialized,
Running,
Asleep,
ShuttingDown,
}
#[repr(transparent)]
pub struct AtomicControl {
raw: AtomicU32,
}
impl AtomicControl {
pub const fn new() -> Self {
Self {
raw: AtomicU32::new(4),
}
}
pub fn initialize(&self) {
self.raw
.compare_exchange(4, 0, Ordering::Relaxed, Ordering::Relaxed)
.expect("Workers are never allocated the same IDs");
}
pub unsafe fn set_self_asleep(&self, waker_slot: &WakerSlot, waker: Waker) {
unsafe { waker_slot.insert(waker) };
self.raw.store(3, Ordering::Release);
}
pub unsafe fn try_wake(&self, waker_slot: &WakerSlot) -> bool {
match self.raw.compare_exchange(
3,
1,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(_) => {}
Err(_) => return false,
}
let waker = unsafe { waker_slot.extract() };
let prev = self.raw.swap(0, Ordering::Release);
if prev == 2 {
atomic_wait::wake_one(&self.raw);
}
waker.wake();
true
}
unsafe fn lock_self_asleep(&self) -> ControlFlow<()> {
match self.raw.compare_exchange(
3,
1,
Ordering::Relaxed,
Ordering::Acquire,
) {
Ok(3) => {
ControlFlow::Continue(())
}
Err(0) => {
ControlFlow::Break(())
}
Err(1) => {
let backoff = Backoff::new();
while !backoff.is_completed() {
backoff.snooze();
match self.raw.load(Ordering::Acquire) {
0 => return ControlFlow::Break(()),
1 => {}
_ => unsafe { unreachable_unchecked() },
}
}
match self.raw.compare_exchange(
1,
2,
Ordering::Relaxed,
Ordering::Acquire,
) {
Ok(_) => {}
Err(0) => return ControlFlow::Break(()),
_ => unsafe { unreachable_unchecked() },
}
loop {
atomic_wait::wait(&self.raw, 2);
match self.raw.load(Ordering::Acquire) {
0 => return ControlFlow::Break(()),
2 => continue,
_ => unsafe { unreachable_unchecked() },
}
}
}
_ => unsafe { unreachable_unchecked() },
}
}
pub unsafe fn set_self_awake(&self, waker_slot: &WakerSlot) {
match unsafe { self.lock_self_asleep() } {
ControlFlow::Continue(()) => {}
ControlFlow::Break(()) => return,
}
let _ = unsafe { waker_slot.extract() };
self.raw.store(0, Ordering::Release);
}
pub unsafe fn poll_asleep(
&self,
waker: &Waker,
waker_slot: &WakerSlot,
) -> Control {
match unsafe { self.lock_self_asleep() } {
ControlFlow::Continue(()) => {}
ControlFlow::Break(()) => return Control::Running,
}
let waker = waker.clone();
let _ = unsafe { waker_slot.replace(waker) };
self.raw.store(3, Ordering::Release);
Control::Asleep
}
}
impl Default for AtomicControl {
fn default() -> Self {
Self::new()
}
}
#[repr(transparent)]
pub struct WakerSlot {
raw: UnsafeCell<Option<Waker>>,
}
impl WakerSlot {
pub const fn new() -> Self {
Self {
raw: UnsafeCell::new(None),
}
}
pub unsafe fn insert(&self, waker: Waker) {
debug_assert!(unsafe { &*self.raw.get() }.is_none());
unsafe { self.raw.get().write(Some(waker)) };
}
pub unsafe fn replace(&self, waker: Waker) -> Waker {
debug_assert!(unsafe { &*self.raw.get() }.is_some());
let slot = unsafe { (*self.raw.get()).as_mut().unwrap_unchecked() };
core::mem::replace(slot, waker)
}
pub unsafe fn extract(&self) -> Waker {
unsafe { (*self.raw.get()).take().unwrap_unchecked() }
}
}
impl Default for WakerSlot {
fn default() -> Self {
Self::new()
}
}
unsafe impl Send for WakerSlot {}
unsafe impl Sync for WakerSlot {}