use core::{
any::Any,
cell::Cell,
fmt,
hint::unreachable_unchecked,
panic::Location,
pin::Pin,
ptr::NonNull,
task::{Context, Poll},
};
use intrusive_collections::{LinkedList, RBTreeLink};
pub use adapter::{Adapter, PointerOps};
pub use puck::Puck;
pub use share::{Label, Share};
use crate::{
Dispatch, ExitStatus,
calendar::{PlanState, Scheduler},
config::Config,
error::NotIdle,
fsm::*,
ptr::{IntrusivelyCounted, Irc, IrcBox, IrcBoxed},
simulator::{Mark, Prec},
};
mod adapter;
mod puck;
mod share;
pub type Link = RBTreeLink;
pub struct Continuation<'brand, C: ?Sized + Config> {
hook: Link,
task_box: IrcBox<ContBox>,
share: NonNull<Share<C>>,
pending: Cell<LinkedList<Adapter<C>>>,
prec: Cell<Prec>,
state: StateMachine<'brand, State<C>>,
#[cfg(feature = "tracing")]
span: tracing::Span,
}
impl<C: ?Sized + Config> Continuation<'static, C> {
pub(crate) fn new(prec: Prec, location: &'static Location<'static>) -> Self {
Self {
hook: Link::new(),
task_box: IrcBox::with_location(ContBox::new(), location),
share: NonNull::dangling(),
pending: Cell::new(LinkedList::new(Adapter::NEW)),
prec: Cell::new(prec),
state: StateMachine::default(),
#[cfg(feature = "tracing")]
span: tracing::Span::current(),
}
}
pub(crate) fn result(&self) -> Option<ExitStatus> {
self.brand(|task, once| {
task.token(once)
.into_term()
.map(|state| task.branded_result(&state))
.ok()
})
}
pub(crate) fn wake(this: Irc<Self>) -> Result<(), NotIdle> {
this.clone().brand(|task, once| {
let idle = task.token(once).into_idle()?;
task.branded_share(&idle)
.sim()
.calendar()
.activate(task.clone(), idle);
Ok(())
})
}
pub(crate) fn time(&self) -> Option<C::Time> {
self.brand(|task, once| {
task.token(once)
.into_next()
.ok()
.map(|next| task.next_time(&next))
})
}
}
impl<'brand, C: ?Sized + Config> Continuation<'brand, C> {
#[cfg(feature = "tracing")]
pub fn enter_span(&self) -> tracing::span::Entered<'_> {
self.span.enter()
}
#[cfg(not(feature = "tracing"))]
pub const fn enter_span(&self) {}
pub(crate) unsafe fn set_vptr(&self, vptr: NonNull<dyn Dispatch + '_>) {
unsafe {
self.task_box.set_vptr(vptr);
}
}
pub(crate) fn clear_vptr(&self) {
self.task_box.clear_vptr();
}
pub(crate) fn use_count(&self) -> usize {
self.task_box.refs.get()
}
pub(crate) fn state(&self) -> &StateMachine<'brand, State<C>> {
&self.state
}
pub(crate) fn prec(&self) -> Prec {
self.prec.get()
}
pub(crate) fn set_prec(&self, prec: Prec) {
self.prec.set(prec);
}
#[inline]
pub(crate) fn insert_pending(&self, other: Irc<Continuation<'static, C>>) {
let mut list = self.pending.take();
list.push_back(other);
self.pending.set(list);
}
pub(crate) unsafe fn remove_pending(&self, other: &Continuation<'static, C>) {
if other.hook.is_linked() {
let mut list = self.pending.take();
unsafe {
list.cursor_mut_from_ptr(other.detach()).remove();
}
self.pending.set(list);
}
}
pub(crate) fn wake_pending(&self) {
for task in self.pending.take() {
Continuation::wake(task).ok();
}
}
pub(crate) fn detach(&self) -> &Continuation<'static, C> {
unsafe { core::mem::transmute(self) }
}
pub(crate) fn location(&self) -> &'static Location<'static> {
IrcBox::location(&self.task_box)
}
unsafe fn is_same_thread(&self) -> bool {
let share = unsafe { self.share.as_ref() };
crate::erased::with(|sim| core::ptr::addr_eq(&**share.sim(), sim)).unwrap_or(false)
}
pub(crate) fn token(&self, once: Ephemeral<'brand>) -> token::State<'brand> {
self.state.token(once)
}
pub(crate) unsafe fn bind(
mut self: Pin<&mut Self>,
born: token::Born<'brand>,
share: &Share<C>,
) -> token::Idle<'brand> {
self.share = NonNull::from(share);
let _span = self.enter_span();
self.state.transition(born, ())
}
pub(crate) fn deschedule(&self, next: token::Next<'brand>) -> token::Idle<'brand> {
let share = self.branded_share(&next);
share.sim().calendar().remove(self, next)
}
pub(crate) fn deactivate(&self, busy: token::Busy<'brand>) -> token::Idle<'brand> {
let share = self.branded_share(&busy);
share.sim().unslot(self, busy)
}
pub(crate) fn poll(&self, busy: token::Busy<'brand>, cx: &mut Context<'_>) -> Poll<()> {
let vtab = unsafe { Pin::new_unchecked(self.task_box.vptr.get().unwrap().as_ref()) };
let (once, res) = self.state.debrand(busy, move |_| vtab.poll(cx));
match self.token(once).into_busy() {
Ok(busy) => match res {
Poll::Ready(result) => {
let _: token::Done<'_> = self.state.transition(busy, result);
self.wake_pending();
Poll::Ready(())
}
Poll::Pending => {
let _: token::Idle<'_> = self.state.transition(busy, ());
Poll::Pending
}
},
Err(_err) => {
debug_assert!(
res.is_pending(),
"task in state `{:?}` should not have been able to terminate",
_err.0
);
Poll::Pending
}
}
}
pub(crate) fn branded_share<'s, I>(&'s self, init: &I) -> &'s Share<C>
where
I: Into<token::Init<'brand>>,
{
let _ = init;
unsafe { self.share.as_ref() }
}
pub(crate) fn share(&self) -> Option<&Share<C>> {
if self.state().erased().is_init() {
Some(unsafe { self.share.as_ref() })
} else {
None
}
}
pub(crate) fn next_state<F, R>(&self, next: &token::Next<'brand>, f: F) -> R
where
F: FnOnce(&<C::Plan as Scheduler>::State) -> R,
{
let _ = next;
match &*self.state.borrow() {
State::Next(state) => f(state),
_ => unsafe { unreachable_unchecked() },
}
}
pub(crate) fn next_time(&self, next: &token::Next<'brand>) -> C::Time {
self.next_state(next, |s| s.time())
}
pub(crate) fn mark<'s, I>(&'s self, init: &I) -> &'s Cell<Mark>
where
I: Into<token::Init<'brand>>,
{
self.branded_share(init).mark()
}
pub(crate) fn branded_result<T>(&self, _: &T) -> ExitStatus
where
T: Into<token::Term<'brand>>,
{
match &*self.state.borrow() {
State::Done(rc) | State::Gone(rc) => *rc,
_ => unsafe { unreachable_unchecked() },
}
}
}
impl<'b, C: ?Sized + Config> Stateful for Continuation<'b, C> {
type Brand = &'b ();
unsafe fn enter(&self) {
unsafe {
self.state.enter();
}
}
unsafe fn leave(&self) {
unsafe {
self.state.leave();
}
}
}
impl<'b, C: ?Sized + Config> Rebrand<'b> for Continuation<'b, C> {
type Kind<'a> = Continuation<'a, C>;
}
impl<C: ?Sized + Config> Unpin for Continuation<'_, C> {}
impl<C: ?Sized + Config> fmt::Debug for Continuation<'_, C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("Continuation");
s.field("state", &self.state.borrow())
.field("location", self.location());
self.brand(|task, once| {
struct PrettyName(Label);
impl fmt::Debug for PrettyName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{}\"", &self.0)
}
}
let state = task.token(once);
let init = state.as_init()?;
s.field("label", &PrettyName(task.branded_share(init).label()))
.field("rank", &task.branded_share(init).rank());
None::<()>
});
s.field("prec", &self.prec().float_range())
.field("refs", &self.use_count())
.finish()
}
}
impl<C: Config> crate::erased::Continuation for Continuation<'static, C> {
fn subject(&self) -> &dyn Any {
self.share().map_or(&(), |shared| shared.subject())
}
fn label(&self) -> Label {
self.share()
.map_or(Label::default(), |shared| shared.label())
}
fn prec(&self) -> Prec {
self.prec()
}
fn state(&self) -> erased::State {
self.state().borrow().erased()
}
}
unsafe impl<C: ?Sized + Config> IntrusivelyCounted for Continuation<'_, C> {
fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
&self.task_box
}
}
pub struct ContBox {
refs: Cell<usize>,
vptr: Cell<Option<NonNull<dyn Dispatch>>>,
}
impl ContBox {
const fn new() -> Self {
ContBox {
refs: Cell::new(0),
vptr: Cell::new(None),
}
}
unsafe fn set_vptr(&self, vptr: NonNull<dyn Dispatch + '_>) {
unsafe {
use core::mem::transmute;
self.vptr.set(Some(transmute::<
NonNull<dyn Dispatch + '_>,
NonNull<dyn Dispatch + 'static>,
>(vptr)));
}
}
fn clear_vptr(&self) {
self.vptr.set(None);
}
}
unsafe impl IrcBoxed for ContBox {
fn ref_count(&self) -> usize {
self.refs.get()
}
fn acquire(&self, _: crate::ptr::Private) {
self.refs.set(self.refs.get() + 1);
}
fn release(&self, _: crate::ptr::Private) {
self.refs.set(self.refs.get() - 1);
}
fn reclaim(&self, _: crate::ptr::Private) -> Option<fn(NonNull<dyn IrcBoxed>)> {
self.vptr.get().is_some().then_some(
|this| unsafe {
let this = this.cast::<Self>().as_ref();
let mut vptr = this.vptr.get().unwrap_unchecked();
Pin::new_unchecked(vptr.as_mut()).reclaim();
},
)
}
}
fsm! {
#[derive(Default)]
pub enum State<C: Config> {
#[default]
Born -> {Idle, Gone},
Idle -> {Next, Gone},
Busy -> {Idle, Done},
Next(<C::Plan as Scheduler>::State) -> {Idle, Busy},
Done(ExitStatus) -> {Gone},
Gone(ExitStatus) -> {}
}
pub Init = {Idle, Busy, Next, Done, Gone};
pub Term: Init = {Done, Gone};
}
impl<C: ?Sized + Config> fmt::Debug for State<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug = f.debug_tuple(self.label());
match self {
State::Next(plan) => debug.field(plan),
State::Done(exit) => debug.field(exit),
State::Gone(exit) => debug.field(exit),
_ => &mut debug,
}
.finish()
}
}