use std::cell::{Cell, UnsafeCell};
use std::marker::PhantomPinned;
use std::ptr::NonNull;
use std::task::{RawWaker, RawWakerVTable, Waker};
use crate::backend::token::{Epoch, LocalIdx, Token};
const _: () = assert!(size_of::<Token>() == size_of::<*const ()>());
pub struct Slot {
target: Cell<Token>,
queued: Cell<bool>,
arena: NonNull<Arena>,
}
impl Slot {
pub(super) fn new(target: Token, arena: NonNull<Arena>) -> Self {
Self {
target: Cell::new(target),
queued: Cell::new(false),
arena,
}
}
pub fn set_target(&self, target: Token) {
self.target.set(target);
}
pub fn make_waker(&self) -> Waker {
unsafe { Waker::from_raw(self.raw_waker()) }
}
pub fn wake_ref(&self) -> WakeRef {
WakeRef(NonNull::from(self))
}
pub fn wake(&self) {
if self.queued.replace(true) {
return;
}
let arena = unsafe { self.arena.as_ref() };
unsafe {
(*arena.live.get()).push(NonNull::from(self));
}
}
fn raw_waker(&self) -> RawWaker {
RawWaker::new(self as *const Slot as *const (), &VTABLE)
}
unsafe fn from_data<'a>(data: *const ()) -> &'a Slot {
unsafe { &*(data as *const Slot) }
}
}
static VTABLE: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_drop);
unsafe fn vt_clone(data: *const ()) -> RawWaker {
unsafe { Slot::from_data(data) }.raw_waker()
}
unsafe fn vt_wake(data: *const ()) {
unsafe { Slot::from_data(data) }.wake();
}
unsafe fn vt_drop(_data: *const ()) {}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct WakeRef(NonNull<Slot>);
impl WakeRef {
pub fn verified(w: &Waker) -> Self {
assert!(
std::ptr::eq(w.vtable(), &VTABLE),
"dope: waker not minted by dope's parker"
);
Self(unsafe { NonNull::new_unchecked(w.data() as *mut Slot) })
}
pub fn wake(&self) {
unsafe { self.0.as_ref().wake() }
}
}
pub trait Parker {
fn slot(&self, slot: crate::backend::socket::FdSlot) -> &Slot;
fn make_slot(&self, target: Token) -> Slot;
fn drain(&self, out: &mut Vec<Token>);
fn is_empty(&self) -> bool;
}
#[repr(C)]
pub(super) struct Arena {
live: UnsafeCell<Vec<NonNull<Slot>>>,
_pin: PhantomPinned,
slots: [Slot],
}
#[repr(C)]
struct ArenaHeader {
live: UnsafeCell<Vec<NonNull<Slot>>>,
_pin: PhantomPinned,
}
impl Arena {
pub(super) fn new(slots: usize) -> std::io::Result<Box<Self>> {
use std::alloc::{Layout, alloc, handle_alloc_error};
use std::io;
let layout_err =
|_| io::Error::new(io::ErrorKind::InvalidInput, "park::Arena layout overflow");
let header = Layout::new::<ArenaHeader>();
let tail = Layout::array::<Slot>(slots).map_err(layout_err)?;
let (combined, slots_offset) = header.extend(tail).map_err(layout_err)?;
let final_layout = combined.pad_to_align();
let boxed = unsafe {
let raw = alloc(final_layout);
if raw.is_null() {
handle_alloc_error(final_layout);
}
std::ptr::write(
raw as *mut UnsafeCell<Vec<NonNull<Slot>>>,
UnsafeCell::new(Vec::with_capacity(slots)),
);
let fat: *mut Arena =
std::ptr::slice_from_raw_parts_mut(raw as *mut Slot, slots) as *mut Arena;
let arena_ptr = NonNull::new_unchecked(fat);
let dummy = Token::new(0, LocalIdx::new(0), Epoch::INITIAL);
let slots_start = raw.add(slots_offset) as *mut Slot;
for i in 0..slots {
std::ptr::write(slots_start.add(i), Slot::new(dummy, arena_ptr));
}
Box::from_raw(fat)
};
Ok(boxed)
}
pub(super) fn slot(&self, slot: crate::backend::socket::FdSlot) -> &Slot {
&self.slots[slot.raw() as usize]
}
pub(super) fn is_empty(&self) -> bool {
unsafe { (*self.live.get()).is_empty() }
}
pub(super) fn drain(&self, out: &mut Vec<Token>) {
let live = unsafe { &mut *self.live.get() };
for slot_ptr in live.drain(..) {
let slot = unsafe { slot_ptr.as_ref() };
slot.queued.set(false);
out.push(slot.target.get());
}
}
}