use core::future::Future;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::pin::Pin;
use core::ptr::{self, NonNull};
use core::task::{Context, Poll};
use super::{ORDER_END, VACANT_END};
pub(super) struct FutureSlot<Fut> {
storage: MaybeUninit<Fut>,
state: SlotState,
metadata: usize,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
enum SlotState {
Vacant,
Pending,
Detached,
Completed,
}
impl<Fut> FutureSlot<Fut> {
const fn empty(vacant_next: usize) -> Self {
Self {
storage: MaybeUninit::uninit(),
state: SlotState::Vacant,
metadata: vacant_next,
}
}
unsafe fn state(slot: *mut Self) -> SlotState {
unsafe { (&raw const (*slot).state).read() }
}
unsafe fn set_state(slot: *mut Self, state: SlotState) {
unsafe { (&raw mut (*slot).state).write(state) };
}
unsafe fn metadata(slot: *mut Self) -> usize {
unsafe { (&raw const (*slot).metadata).read() }
}
unsafe fn replace_metadata(slot: *mut Self, metadata: usize) -> usize {
unsafe { ptr::replace(&raw mut (*slot).metadata, metadata) }
}
unsafe fn storage(slot: *mut Self) -> *mut Fut {
unsafe { (&raw mut (*slot).storage).cast::<Fut>() }
}
}
impl<Fut> Drop for FutureSlot<Fut> {
fn drop(&mut self) {
if self.state == SlotState::Pending {
self.state = SlotState::Detached;
unsafe { ptr::drop_in_place(self.storage.as_mut_ptr()) };
}
}
}
pub(super) struct SlotSlab<Fut> {
slots: NonNull<[FutureSlot<Fut>]>,
owns: PhantomData<Box<[FutureSlot<Fut>]>>,
}
unsafe impl<Fut: Send> Send for SlotSlab<Fut> {}
unsafe impl<Fut: Sync> Sync for SlotSlab<Fut> {}
impl<Fut> SlotSlab<Fut> {
pub(super) fn new(len: usize) -> Self {
let slots = (0..len)
.map(|index| {
let next = if index + 1 == len {
VACANT_END
} else {
index + 1
};
FutureSlot::empty(next)
})
.collect::<Vec<_>>()
.into_boxed_slice();
let slots = NonNull::new(Box::into_raw(slots))
.expect("invariant: a boxed slice allocation is never null");
Self {
slots,
owns: PhantomData,
}
}
pub(super) const fn len(&self) -> usize {
self.slots.len()
}
fn slot(&self, index: usize) -> *mut FutureSlot<Fut> {
assert!(
index < self.len(),
"invariant: retained slot index is in bounds"
);
unsafe { self.slots.as_ptr().cast::<FutureSlot<Fut>>().add(index) }
}
pub(super) fn is_pollable(&self, index: usize) -> bool {
let slot = self.slot(index);
unsafe { FutureSlot::state(slot) == SlotState::Pending }
}
pub(super) fn insert(&mut self, index: usize, future: Fut) {
let slot = self.slot(index);
unsafe {
debug_assert_eq!(
FutureSlot::state(slot),
SlotState::Vacant,
"retained future slot must be vacant"
);
debug_assert_eq!(
FutureSlot::metadata(slot),
VACANT_END,
"retained future slot must be detached from the vacancy list"
);
}
unsafe { FutureSlot::storage(slot).write(future) };
unsafe {
FutureSlot::replace_metadata(slot, ORDER_END);
FutureSlot::set_state(slot, SlotState::Pending);
}
}
pub(super) fn take_vacant_next(&mut self, index: usize) -> usize {
let slot = self.slot(index);
unsafe {
debug_assert_eq!(FutureSlot::state(slot), SlotState::Vacant);
FutureSlot::replace_metadata(slot, VACANT_END)
}
}
pub(super) fn return_to_vacant(&mut self, index: usize, next: usize) {
let slot = self.slot(index);
unsafe {
debug_assert_eq!(FutureSlot::state(slot), SlotState::Detached);
debug_assert_eq!(
FutureSlot::metadata(slot),
VACANT_END,
"returned future slot must not already be vacant"
);
FutureSlot::replace_metadata(slot, next);
FutureSlot::set_state(slot, SlotState::Vacant);
}
}
pub(super) fn set_order_next(&mut self, index: usize, next: usize) {
let slot = self.slot(index);
unsafe {
debug_assert!(matches!(
FutureSlot::state(slot),
SlotState::Pending | SlotState::Completed
));
debug_assert_eq!(FutureSlot::metadata(slot), ORDER_END);
FutureSlot::replace_metadata(slot, next);
}
}
pub(super) fn order_next(&self, index: usize) -> usize {
let slot = self.slot(index);
unsafe {
debug_assert!(matches!(
FutureSlot::state(slot),
SlotState::Pending | SlotState::Detached | SlotState::Completed
));
FutureSlot::metadata(slot)
}
}
pub(super) fn mark_completed(&mut self, index: usize) {
let slot = self.slot(index);
unsafe {
debug_assert_eq!(FutureSlot::state(slot), SlotState::Detached);
FutureSlot::set_state(slot, SlotState::Completed);
}
}
pub(super) fn take_completed_next(&mut self, index: usize) -> Option<usize> {
let slot = self.slot(index);
unsafe {
if FutureSlot::state(slot) != SlotState::Completed {
return None;
}
FutureSlot::set_state(slot, SlotState::Detached);
Some(FutureSlot::replace_metadata(slot, ORDER_END))
}
}
}
impl<Fut> SlotSlab<Fut>
where
Fut: Future,
{
pub(super) fn poll(&mut self, index: usize, context: &mut Context<'_>) -> Poll<Fut::Output> {
let slot = self.slot(index);
let future = unsafe {
debug_assert_eq!(FutureSlot::state(slot), SlotState::Pending);
FutureSlot::storage(slot)
};
let poll = unsafe { Pin::new_unchecked(&mut *future) }.poll(context);
match poll {
Poll::Ready(output) => {
unsafe { FutureSlot::set_state(slot, SlotState::Detached) };
unsafe { ptr::drop_in_place(future) };
Poll::Ready(output)
}
Poll::Pending => Poll::Pending,
}
}
}
impl<Fut> Drop for SlotSlab<Fut> {
fn drop(&mut self) {
drop(unsafe { Box::from_raw(self.slots.as_ptr()) });
}
}