use core::mem;
use core::sync::atomic::{self, AtomicU8, Ordering};
use parking_lot_core::{DEFAULT_PARK_TOKEN, DEFAULT_UNPARK_TOKEN};
#[repr(transparent)]
pub struct OnceLock(AtomicU8);
impl OnceLock {
const DONE: u8 = 1;
const LOCKED: u8 = 2;
const WAITING: u8 = 4;
const EPOCH_1: u8 = 8;
const EPOCH_MASK: u8 = !(Self::DONE | Self::LOCKED | Self::WAITING);
#[inline(always)]
const fn next_epoch(current_state: u8) -> u8 {
(current_state & Self::EPOCH_MASK).wrapping_add(Self::EPOCH_1) & Self::EPOCH_MASK
}
#[inline]
pub(crate) const fn new() -> Self {
Self(AtomicU8::new(0)) }
#[inline]
pub(crate) const fn done() -> Self {
Self(AtomicU8::new(Self::DONE)) }
#[inline]
fn notify_all(&self) {
unsafe {
parking_lot_core::unpark_all(self.0.as_ptr() as usize, DEFAULT_UNPARK_TOKEN);
}
}
#[inline]
fn wait(&self, expected_state: u8) {
unsafe {
let _ = parking_lot_core::park(
self.0.as_ptr() as usize,
|| self.0.load(atomic::Ordering::Acquire) == expected_state, || {}, |_, _| {}, DEFAULT_PARK_TOKEN, None, );
}
}
#[inline]
pub(crate) fn set_done(&self) -> bool {
let current_state = self.0.load(Ordering::Relaxed);
let next_epoch = Self::next_epoch(current_state);
let new_state = Self::DONE | next_epoch;
let prev_state = self.0.swap(new_state, Ordering::Release);
if prev_state & Self::WAITING != 0 {
self.notify_all();
}
prev_state & Self::DONE == 0
}
#[inline]
pub(crate) fn set_uninit(&self) -> bool {
let current_state = self.0.load(Ordering::Relaxed);
let next_epoch = Self::next_epoch(current_state);
let new_state = next_epoch;
let prev_state = self.0.swap(new_state, Ordering::Release);
if prev_state & Self::WAITING != 0 {
self.notify_all();
}
prev_state & Self::DONE != 0
}
#[inline]
pub(crate) fn is_done(&self, ordering: Ordering) -> bool {
self.0.load(ordering) & Self::DONE != 0
}
#[inline]
fn lock_step(&self, nowait: bool) -> Result<Option<OnceGuard<'_>>, u8> {
loop {
let current_state = self.0.load(Ordering::Relaxed);
if current_state & Self::DONE != 0 {
return Ok(None);
}
if current_state & Self::LOCKED == 0 {
let new_state = current_state | Self::LOCKED;
match self.0.compare_exchange_weak(
current_state,
new_state,
Ordering::Acquire, Ordering::Relaxed,
) {
Ok(_) => return Ok(Some(OnceGuard::new(self))), Err(_) => {
std::hint::spin_loop();
continue;
}
}
}
if !nowait && (current_state & Self::WAITING == 0) {
let new_state = current_state | Self::WAITING;
match self.0.compare_exchange_weak(
current_state,
new_state,
Ordering::Relaxed, Ordering::Relaxed,
) {
Ok(_) => {
return Err(new_state);
}
Err(_) => {
std::hint::spin_loop();
continue;
}
}
}
return Err(current_state);
}
}
#[inline]
pub(crate) fn lock(&self) -> Option<OnceGuard<'_>> {
match self.lock_step(false) {
Ok(guard_opt) => guard_opt, Err(mut state_when_failed) => {
loop {
self.wait(state_when_failed);
match self.lock_step(false) {
Ok(guard_opt) => return guard_opt,
Err(new_state) => {
state_when_failed = new_state;
}
}
}
}
}
}
#[inline]
pub(crate) async fn lock_async(&self) -> Option<OnceGuard<'_>> {
#[allow(clippy::never_loop)]
loop {
for _ in 0..16 {
match self.lock_step(false) {
Ok(guard_opt) => return guard_opt,
Err(state) => {
for _ in 0..32 {
#[cfg(any(feature = "async-tokio", feature = "async-tokio-mt"))]
tokio::task::yield_now().await;
if self.0.load(Ordering::Relaxed) != state {
break; }
}
}
}
}
#[cfg(feature = "async-tokio-mt")]
{
return match self.lock_step(false) {
Ok(x) => x,
Err(state) => tokio::task::block_in_place(|| {
self.wait(state);
self.lock()
}),
};
}
}
}
#[inline]
pub(crate) fn try_lock(&self) -> Option<OnceGuard<'_>> {
self.lock_step(true).ok().flatten()
}
}
pub struct OnceGuard<'a> {
state: &'a OnceLock,
}
impl<'a> OnceGuard<'a> {
#[inline(always)]
pub(crate) const fn new(state: &'a OnceLock) -> Self {
Self { state }
}
#[inline(always)]
pub(crate) fn commit(self) -> bool {
let success = self.state.set_done();
mem::forget(self); success
}
#[inline(always)]
pub(crate) unsafe fn commit_forgotten(&self) {
self.state.set_done();
}
}
impl Drop for OnceGuard<'_> {
#[inline(always)]
fn drop(&mut self) {
self.state.set_uninit(); }
}