use crate::{
ExitStatus,
config::Config,
continuation::Share,
continuation::{Continuation, Label, erased::State, token},
error::{NotIdle, NotInit},
fsm::*,
ptr::AsIrc,
ptr::Irc,
simulator::{Prec, Sim},
};
use core::{
any::Any,
fmt,
future::IntoFuture,
mem::ManuallyDrop,
panic::Location,
ptr::NonNull,
task::{RawWaker, RawWakerVTable},
};
pub struct Puck<C: ?Sized + Config>(Irc<Continuation<'static, C>>);
impl<C: ?Sized + Config> Puck<C> {
pub(crate) fn new<'b, T>(task: Irc<Continuation<'b, C>>, _: &T) -> Self
where
T: Into<token::Init<'b>>,
{
Puck(Irc::map(task, Continuation::detach))
}
pub(crate) fn checked(task: Irc<Continuation<'static, C>>) -> Result<Self, NotInit> {
let state = task.state().erased();
state.is_init().then(|| Puck(task)).ok_or(NotInit(state))
}
pub(crate) fn into_inner(self) -> Irc<Continuation<'static, C>> {
self.0
}
pub(crate) fn share(&self) -> &Share<C> {
unsafe { self.0.share().unwrap_unchecked() }
}
pub(crate) fn is_same(&self, other: &Continuation<'_, C>) -> bool {
core::ptr::eq(&*self.0, other.detach())
}
}
impl<C: Config> Puck<C> {
const VTABLE: RawWakerVTable = RawWakerVTable::new(
Self::waker_clone,
Self::waker_wake,
Self::waker_wake_by_ref,
Self::waker_drop,
);
pub(crate) fn into_waker(self) -> RawWaker {
RawWaker::new(Irc::into_raw(self.0).cast().as_ptr(), &Self::VTABLE)
}
unsafe fn restore(task_ptr: NonNull<Continuation<'static, C>>) -> Self {
let task = unsafe { task_ptr.as_ref() };
let same_thread = task.brand(|task, once| {
let init = unsafe { task.token(once).into_init().unwrap_unchecked() };
task.is_same_thread(&init)
});
assert!(
same_thread,
"simulation-associated waker may not be moved across thread boundaries"
);
Puck(unsafe { Irc::from_raw(task_ptr) })
}
unsafe fn waker_clone(task: *const ()) -> RawWaker {
unsafe {
let puck = ManuallyDrop::new(Self::restore(
NonNull::new(task as *mut Continuation<'static, C>).unwrap(),
));
ManuallyDrop::into_inner(puck.clone()).into_waker()
}
}
unsafe fn waker_wake(task: *const ()) {
unsafe {
Continuation::wake(
Self::restore(NonNull::new(task as *mut Continuation<'static, C>).unwrap()).0,
)
.ok();
}
}
unsafe fn waker_wake_by_ref(task: *const ()) {
unsafe {
let puck = ManuallyDrop::new(Self::restore(
NonNull::new(task as *mut Continuation<'static, C>).unwrap(),
));
Continuation::wake(puck.0.clone()).ok();
}
}
unsafe fn waker_drop(task: *const ()) {
unsafe {
Self::restore(NonNull::new(task as *mut Continuation<'static, C>).unwrap());
}
}
}
impl<C: ?Sized + Config> crate::Puck<C> for Puck<C> {
fn result(&mut self) -> Option<ExitStatus> {
self.0.result()
}
fn wake(&mut self) -> Result<(), NotIdle> {
Continuation::wake(self.0.clone())
}
fn subject(&self) -> &dyn Any {
self.share().item()
}
fn sim(&self) -> &Sim<C> {
self.share().sim()
}
fn label(&self) -> Label {
self.share().label()
}
fn time(&self) -> Option<C::Time> {
self.0.time()
}
fn rank(&self) -> C::Rank {
self.share().rank()
}
fn prec(&self) -> Prec {
self.0.prec()
}
fn state(&self) -> State {
self.0.state().borrow().erased()
}
fn location(&self) -> &'static Location<'static> {
self.0.location()
}
}
impl<C: ?Sized + Config> AsRef<Continuation<'static, C>> for Puck<C> {
fn as_ref(&self) -> &Continuation<'static, C> {
&self.0
}
}
impl<C: ?Sized + Config> AsIrc<Continuation<'static, C>> for Puck<C> {
fn as_irc(&self) -> Irc<Continuation<'static, C>> {
self.0.clone()
}
}
impl<C: ?Sized + Config> IntoFuture for Puck<C> {
type Output = ExitStatus;
type IntoFuture = crate::ops::Join<C, Self>;
fn into_future(self) -> Self::IntoFuture {
crate::ops::join(self)
}
}
impl<C: ?Sized + Config> fmt::Debug for Puck<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<C: ?Sized + Config> Clone for Puck<C> {
fn clone(&self) -> Self {
Puck(self.0.clone())
}
}
impl<C: ?Sized + Config> PartialEq for Puck<C> {
fn eq(&self, other: &Self) -> bool {
self.is_same(&other.0)
}
}
impl<C: ?Sized + Config> Eq for Puck<C> {}