use core::{
convert::{TryFrom, TryInto},
sync::atomic::{AtomicUsize, Ordering},
};
#[cfg(feature = "std")]
use crate::park::StackWaiter;
use crate::POISON_PANIC_MSG;
use self::OnceState::{Ready, Uninit, WouldBlock};
const WOULD_BLOCK: usize = 0;
const UNINIT: usize = 1;
const READY: usize = 2;
const POISONED: usize = 3;
#[derive(Debug)]
pub struct AtomicOnceState(AtomicUsize);
impl AtomicOnceState {
#[inline]
pub(crate) const fn new() -> Self {
Self(AtomicUsize::new(UNINIT))
}
#[inline]
pub(crate) const fn ready() -> Self {
Self(AtomicUsize::new(READY))
}
#[inline]
pub(crate) fn load(&self, order: Ordering) -> Result<OnceState, PoisonError> {
self.0.load(order).try_into()
}
#[inline]
pub(crate) fn try_block(&self, order: Ordering) -> Result<(), TryBlockError> {
let prev = match self.0.compare_exchange(UNINIT, WOULD_BLOCK, order, Ordering::Relaxed) {
Ok(prev) => prev,
Err(prev) => prev,
};
match prev.try_into().expect(POISON_PANIC_MSG) {
Uninit => Ok(()),
Ready => Err(TryBlockError::AlreadyInit),
WouldBlock(state) => Err(TryBlockError::WouldBlock(state)),
}
}
#[inline]
pub(crate) unsafe fn unblock(&self, state: SwapState, order: Ordering) -> BlockedState {
BlockedState(self.0.swap(state as usize, order))
}
}
#[cfg(feature = "std")]
impl AtomicOnceState {
#[inline]
pub(crate) unsafe fn try_enqueue_waiter(
&self,
current: BlockedState,
new: BlockedState,
success: Ordering,
) -> Result<(), OnceState> {
let prev =
match self.0.compare_exchange(current.into(), new.into(), success, Ordering::Relaxed) {
Ok(prev) => prev,
Err(prev) => prev,
};
match prev {
prev if prev == current.into() => Ok(()),
prev => Err(prev.try_into().expect(POISON_PANIC_MSG)),
}
}
}
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct BlockedState(usize);
impl From<BlockedState> for usize {
#[inline]
fn from(state: BlockedState) -> Self {
state.0
}
}
#[cfg(feature = "std")]
impl BlockedState {
pub(crate) fn as_ptr(self) -> *const StackWaiter {
self.0 as *const _
}
}
#[cfg(feature = "std")]
impl From<*const StackWaiter> for BlockedState {
#[inline]
fn from(waiter: *const StackWaiter) -> Self {
Self(waiter as usize)
}
}
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub(crate) enum OnceState {
Uninit,
Ready,
WouldBlock(BlockedState),
}
impl TryFrom<usize> for OnceState {
type Error = PoisonError;
#[inline]
fn try_from(value: usize) -> Result<Self, Self::Error> {
match value {
POISONED => Err(PoisonError),
UNINIT => Ok(Uninit),
READY => Ok(Ready),
state => Ok(WouldBlock(BlockedState(state))),
}
}
}
impl From<OnceState> for usize {
#[inline]
fn from(state: OnceState) -> Self {
match state {
OnceState::Ready => READY,
OnceState::Uninit => UNINIT,
OnceState::WouldBlock(BlockedState(state)) => state,
}
}
}
pub(crate) enum TryBlockError {
AlreadyInit,
WouldBlock(BlockedState),
}
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub(crate) struct PoisonError;
#[repr(usize)]
pub(crate) enum SwapState {
Ready = READY,
Poisoned = POISONED,
}