use std::{
cell::UnsafeCell,
mem::MaybeUninit,
sync::atomic::{AtomicU8, Ordering},
task::Waker,
};
const ASYNC_RESULT_PENDING: u8 = 0;
const ASYNC_RESULT_WAITING: u8 = 1;
const ASYNC_RESULT_UPDATING_WAKER: u8 = 2;
const ASYNC_RESULT_WRITING: u8 = 3;
const ASYNC_RESULT_READY: u8 = 4;
const ASYNC_RESULT_TAKEN: u8 = 5;
pub(super) struct AsyncResultSlot<T> {
result: UnsafeCell<MaybeUninit<T>>,
state: AtomicU8,
waiter: UnsafeCell<MaybeUninit<Waker>>,
}
unsafe impl<T: Send> Send for AsyncResultSlot<T> {}
unsafe impl<T: Send> Sync for AsyncResultSlot<T> {}
impl<T> AsyncResultSlot<T> {
pub(super) fn new() -> Self {
Self {
result: UnsafeCell::new(MaybeUninit::uninit()),
state: AtomicU8::new(ASYNC_RESULT_PENDING),
waiter: UnsafeCell::new(MaybeUninit::uninit()),
}
}
pub(super) fn complete(&self, result: T) {
let Some(waiting) = self.begin_completion() else {
return;
};
unsafe {
(*self.result.get()).write(result);
}
self.state.store(ASYNC_RESULT_READY, Ordering::Release);
if waiting {
let waker = unsafe { (*self.waiter.get()).assume_init_read() };
waker.wake();
}
}
pub(super) fn try_take_ready(&self) -> Option<T> {
if self
.state
.compare_exchange(
ASYNC_RESULT_READY,
ASYNC_RESULT_TAKEN,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
Some(unsafe { (*self.result.get()).assume_init_read() })
} else {
None
}
}
pub(super) fn register_waker(&self, waker: &Waker) {
loop {
match self.state.load(Ordering::Acquire) {
ASYNC_RESULT_PENDING => {
unsafe {
(*self.waiter.get()).write(waker.clone());
}
if self
.state
.compare_exchange(
ASYNC_RESULT_PENDING,
ASYNC_RESULT_WAITING,
Ordering::Release,
Ordering::Acquire,
)
.is_ok()
{
return;
}
unsafe {
(*self.waiter.get()).assume_init_drop();
}
}
ASYNC_RESULT_WAITING => {
if self
.state
.compare_exchange(
ASYNC_RESULT_WAITING,
ASYNC_RESULT_UPDATING_WAKER,
Ordering::Acquire,
Ordering::Acquire,
)
.is_ok()
{
unsafe {
(*self.waiter.get()).assume_init_drop();
(*self.waiter.get()).write(waker.clone());
}
self.state.store(ASYNC_RESULT_WAITING, Ordering::Release);
return;
}
}
ASYNC_RESULT_UPDATING_WAKER | ASYNC_RESULT_WRITING => core::hint::spin_loop(),
_ => return,
}
}
}
fn begin_completion(&self) -> Option<bool> {
loop {
match self.state.load(Ordering::Acquire) {
ASYNC_RESULT_PENDING => {
if self
.state
.compare_exchange(
ASYNC_RESULT_PENDING,
ASYNC_RESULT_WRITING,
Ordering::Relaxed,
Ordering::Acquire,
)
.is_ok()
{
return Some(false);
}
}
ASYNC_RESULT_WAITING => {
if self
.state
.compare_exchange(
ASYNC_RESULT_WAITING,
ASYNC_RESULT_WRITING,
Ordering::Acquire,
Ordering::Acquire,
)
.is_ok()
{
return Some(true);
}
}
ASYNC_RESULT_UPDATING_WAKER => core::hint::spin_loop(),
_ => return None,
}
}
}
}
impl<T> Drop for AsyncResultSlot<T> {
fn drop(&mut self) {
match self.state.load(Ordering::Acquire) {
ASYNC_RESULT_READY => {
unsafe {
self.result.get_mut().assume_init_drop();
}
}
ASYNC_RESULT_WAITING => {
unsafe {
self.waiter.get_mut().assume_init_drop();
}
}
_ => {}
}
}
}