use std::future::Future;
use std::mem::MaybeUninit;
use std::pin::Pin;
use std::task::{Context, Poll};
use super::Fiber;
struct Slot<'d, F: Future> {
cell: MaybeUninit<Fiber<'d, F>>,
}
impl<'d, F: Future> Slot<'d, F> {
const fn vacant() -> Self {
Self {
cell: MaybeUninit::uninit(),
}
}
}
struct Vacant<'s, 'd, F: Future> {
slot: Pin<&'s mut Slot<'d, F>>,
}
struct Filled<'s, 'd, F: Future> {
slot: Pin<&'s mut Slot<'d, F>>,
}
impl<'s, 'd, F: Future> Vacant<'s, 'd, F> {
fn fill(self, fiber: Fiber<'d, F>) -> Filled<'s, 'd, F> {
let mut slot = self.slot;
unsafe { slot.as_mut().get_unchecked_mut().cell.write(fiber) };
Filled { slot }
}
}
impl<'s, 'd, F: Future> Filled<'s, 'd, F> {
unsafe fn assume(slot: Pin<&'s mut Slot<'d, F>>) -> Self {
Self { slot }
}
fn poll(&mut self, cx: &mut Context<'_>) -> Poll<F::Output> {
unsafe {
let fiber = &mut *self.slot.as_mut().get_unchecked_mut().cell.as_mut_ptr();
Pin::new_unchecked(fiber).poll(cx)
}
}
fn clear(self) -> Vacant<'s, 'd, F> {
let mut slot = self.slot;
unsafe { slot.as_mut().get_unchecked_mut().cell.assume_init_drop() };
Vacant { slot }
}
}
pub struct TaskId(u32);
pub struct Slab<'d, F, const N: usize>
where
F: Future + 'd,
{
slots: Pin<Box<[Slot<'d, F>; N]>>,
free: Box<[u32; N]>,
free_len: usize,
}
impl<'d, F, const N: usize> Default for Slab<'d, F, N>
where
F: Future + 'd,
{
fn default() -> Self {
Self::new()
}
}
impl<'d, F, const N: usize> Slab<'d, F, N>
where
F: Future + 'd,
{
pub fn new() -> Self {
const {
assert!(N > 0, "fiber slab capacity must be > 0");
assert!(
N <= u32::MAX as usize,
"fiber slab capacity must be <= u32::MAX"
);
}
let slots: Box<[Slot<'d, F>; N]> = Vec::from_iter((0..N).map(|_| Slot::vacant()))
.into_boxed_slice()
.try_into()
.unwrap_or_else(|_| unreachable!("slots length is exactly N"));
let free: Box<[u32; N]> = Vec::from_iter(0..N as u32)
.into_boxed_slice()
.try_into()
.unwrap_or_else(|_| unreachable!("free length is exactly N"));
Self {
slots: Box::into_pin(slots),
free,
free_len: N,
}
}
fn project(&mut self, idx: u32) -> Pin<&mut Slot<'d, F>> {
unsafe {
let slots = self.slots.as_mut().get_unchecked_mut();
Pin::new_unchecked(&mut slots[idx as usize])
}
}
pub fn alloc(&mut self, fiber: Fiber<'d, F>) -> Option<TaskId> {
if self.free_len == 0 {
return None;
}
self.free_len -= 1;
let idx = self.free[self.free_len];
Vacant {
slot: self.project(idx),
}
.fill(fiber);
Some(TaskId(idx))
}
pub fn poll(&mut self, id: &TaskId, cx: &mut Context<'_>) -> Poll<F::Output> {
let slot = self.project(id.0);
let mut filled = unsafe { Filled::assume(slot) };
filled.poll(cx)
}
pub fn release(&mut self, id: TaskId) {
let slot = self.project(id.0);
let filled = unsafe { Filled::assume(slot) };
filled.clear();
self.free[self.free_len] = id.0;
self.free_len += 1;
}
}
impl<'d, F, const N: usize> Drop for Slab<'d, F, N>
where
F: Future + 'd,
{
fn drop(&mut self) {
self.free[..self.free_len].sort_unstable();
let mut next_free = 0usize;
for idx in 0..N as u32 {
if next_free < self.free_len && self.free[next_free] == idx {
next_free += 1;
continue;
}
let slot = self.project(idx);
let filled = unsafe { Filled::assume(slot) };
filled.clear();
}
}
}