use core::{
any::Any,
cell::RefCell,
fmt,
future::{Future, IntoFuture, Pending},
marker::PhantomData,
mem::ManuallyDrop,
panic::Location,
pin::Pin,
task::{Context, Poll},
};
use crate::{
Active, Dispatch, ExitStatus,
config::{Config, DefaultConfig},
continuation::{Continuation, Label, Share, erased::State, token},
error,
fsm::*,
ptr::{AsIrc, IntrusivelyCounted, Irc, IrcBox, Lease, LeasedMut},
simulator::{Prec, Sim},
};
#[pin_project::pin_project(PinnedDrop, !Unpin)]
pub struct Job<'brand, C: ?Sized + Config, F: Future, T = Unchecked> {
#[pin]
cont: Continuation<'brand, C>,
#[pin]
state: RefCell<Inner<F, T>>,
}
impl<C: ?Sized + Config, F: Future> Job<'static, C, F> {
#[track_caller]
pub fn new<'p, A>(actions: A) -> Lease<'p, Self>
where
A: IntoFuture<IntoFuture = F>,
{
Job::build().with_actions(actions).finish()
}
}
impl Job<'static, DefaultConfig, Pending<()>> {
pub const fn build() -> Builder {
Builder::new()
}
}
impl<C: ?Sized + Config, F: Future, T> Job<'static, C, F, T> {
pub(crate) fn boot<'p>(
this: Pin<LeasedMut<'p, Self>>,
share: Pin<&'p Share<C>>,
) -> Puck<'p, C, F, T>
where
T: Settle<F::Output>,
{
let mut this = Irc::new(this);
unsafe {
let vptr = Irc::into_raw(this);
this = Irc::from_raw(vptr);
this.cont.set_vptr(vptr);
}
this.get_pin_mut().unwrap().brand(move |job, once| {
let born = job.token(once).into_born().unwrap();
unsafe { job.bind(born, share.get_ref()) };
});
Puck(this, PhantomData)
}
}
impl<'brand, C: ?Sized + Config, F: Future, T> Job<'brand, C, F, T> {
pub fn set_prec(&self, prec: Prec) {
self.cont.set_prec(prec);
}
pub fn state(&self) -> State {
self.cont.state().borrow().erased()
}
pub fn result(&self) -> Result<F::Output, error::NotDone> {
self.brand(|job, once| Ok(job.inner_result(job.token(once).into_done()?).1))
}
pub fn abort(&self) {
self.brand(|job, once| {
job.inner_abort(job.token(once));
});
}
pub fn detach(&self) -> &Job<'static, C, F, T> {
unsafe { core::mem::transmute(self) }
}
pub fn token(&self, once: Ephemeral<'brand>) -> token::State<'brand> {
self.cont.token(once)
}
pub fn finalizer(self: Pin<&mut Self>, born: &token::Born<'brand>) -> Pin<&mut T> {
let _ = born;
unsafe { self.map_unchecked_mut(move |job| &mut *job.state.get_mut().future.1) }
}
fn inner_abort(&self, state: token::State<'brand>) -> token::Gone<'brand> {
use token::State::*;
let task = &self.cont;
let _span = task.enter_span();
let once: Ephemeral<'_> = match state {
Gone(gone) => return gone,
Done(done) => return self.inner_result(done).0,
Next(next) => task.deschedule(next).into(),
Busy(busy) => task.deactivate(busy).into(),
state @ (Born(_) | Idle(_)) => state.into(),
};
let (once, ()) = self.debrand(once, |job| unsafe {
job.state.borrow_mut().unchecked_drop();
});
let gone = match task.token(once) {
Born(born) => task.state().transition(born, Err(crate::Failure)),
Idle(idle) => task.state().transition(idle, Err(crate::Failure)),
state => unreachable!(
"expected task to be 'Born' or 'Idle' but it is \
in state '{:?}' instead",
state.erased()
),
};
task.wake_pending();
gone
}
fn inner_result(&self, done: token::Done<'brand>) -> (token::Gone<'brand>, F::Output) {
let task = &self.cont;
let rc = task.branded_result(&done);
let _span = task.enter_span();
unsafe {
(
task.state().transition(done, rc),
self.state
.borrow_mut()
.unchecked_result()
.unwrap_unchecked(),
)
}
}
pub(crate) unsafe fn bind(
self: Pin<&mut Self>,
born: token::Born<'brand>,
share: &Share<C>,
) -> token::Idle<'brand> {
unsafe { self.project().cont.bind(born, share) }
}
}
impl<'b, C: ?Sized + Config, F: Future, T> Stateful for Job<'b, C, F, T> {
type Brand = &'b ();
unsafe fn enter(&self) {
unsafe {
self.cont.enter();
}
}
unsafe fn leave(&self) {
unsafe {
self.cont.leave();
}
}
}
impl<'b, C: ?Sized + Config, F: Future, T> Rebrand<'b> for Job<'b, C, F, T> {
type Kind<'a> = Job<'a, C, F, T>;
}
impl<C: ?Sized + Config, F: Future, T> fmt::Debug for Job<'_, C, F, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("Job");
s.field("continuation", &self.cont)
.field("future", &core::any::type_name::<F>())
.finish()
}
}
impl<C: ?Sized + Config, F: Future, T: Settle<F::Output>> Dispatch for Job<'_, C, F, T> {
fn poll(self: Pin<&Self>, cx: &mut Context<'_>) -> Poll<ExitStatus> {
assert!(
self.state().is_busy(),
"only active continuations can be polled"
);
let mut state = self.state.borrow_mut();
unsafe { Pin::new_unchecked(&mut *state.future.0) }
.poll(cx)
.map(move |rv| {
unsafe { state.unchecked_terminate(rv) }
})
}
}
impl<C: ?Sized + Config, F: Future, T: Settle<F::Output>> Active<C> for Job<'static, C, F, T> {
type Output = F::Output;
type Puck<'p>
= Puck<'p, C, F, T>
where
Self: 'p;
fn bind<'p>(this: Pin<LeasedMut<'p, Self>>, sctx: &'p Share<C>) -> Self::Puck<'p> {
let mut irc = Irc::new(this);
irc.get_pin_mut().unwrap().brand(move |mut job, once| {
let born = job.token(once).into_born().unwrap();
unsafe {
job.as_mut().bind(born, sctx);
}
});
Puck(irc, PhantomData)
}
}
unsafe impl<'brand, C, F, T> IntrusivelyCounted for Job<'brand, C, F, T>
where
C: ?Sized + Config,
F: Future,
{
type Inner = <Continuation<'brand, C> as IntrusivelyCounted>::Inner;
fn irc_box(&self) -> &IrcBox<Self::Inner> {
self.cont.irc_box()
}
}
impl<'brand, C, F, T> AsRef<Continuation<'brand, C>> for Job<'brand, C, F, T>
where
C: ?Sized + Config,
F: Future,
{
fn as_ref(&self) -> &Continuation<'brand, C> {
&self.cont
}
}
#[pin_project::pinned_drop]
impl<C: ?Sized + Config, F: Future, T> PinnedDrop for Job<'_, C, F, T> {
fn drop(self: Pin<&mut Self>) {
self.cont.clear_vptr();
self.abort();
}
}
pub struct Puck<'p, C: ?Sized + Config, F: Future, T = Unchecked>(
Irc<Job<'static, C, F, T>>,
PhantomDrop<Pin<&'p mut Job<'static, C, F, T>>>,
);
impl<C: ?Sized + Config, F: Future, T> Puck<'_, C, F, T> {
pub fn share(&self) -> &Share<C> {
unsafe { self.0.cont.share().unwrap_unchecked() }
}
pub fn abort(self) {
self.0.abort();
}
}
impl<C, F, T> fmt::Debug for Puck<'_, C, F, T>
where
C: ?Sized + Config,
F: Future,
T: Settle<F::Output>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<C, F, T> crate::Puck<C> for Puck<'_, C, F, T>
where
C: ?Sized + Config,
F: Future,
T: Settle<F::Output>,
{
fn result(&mut self) -> Option<Self::Output> {
self.0.result().ok()
}
fn wake(&mut self) -> Result<(), error::NotIdle> {
Continuation::wake(Irc::map(self.0.clone(), |inner| &inner.cont))
}
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.cont.time()
}
fn rank(&self) -> C::Rank {
self.share().rank()
}
fn prec(&self) -> Prec {
self.0.cont.prec()
}
fn state(&self) -> State {
self.0.cont.state().borrow().erased()
}
fn location(&self) -> &'static Location<'static> {
self.0.cont.location()
}
}
impl<C, F, T> From<Puck<'_, C, F, T>> for Irc<Job<'static, C, F, T>>
where
C: ?Sized + Config,
F: Future,
T: Settle<F::Output>,
{
fn from(value: Puck<'_, C, F, T>) -> Self {
value.0
}
}
impl<C, F, T> AsRef<Continuation<'static, C>> for Puck<'_, C, F, T>
where
C: ?Sized + Config,
F: Future,
T: Settle<F::Output>,
{
fn as_ref(&self) -> &Continuation<'static, C> {
&self.0.cont
}
}
impl<C, F, T> AsIrc<Continuation<'static, C>> for Puck<'_, C, F, T>
where
C: ?Sized + Config,
F: Future,
T: Settle<F::Output>,
{
fn as_irc(&self) -> Irc<Continuation<'static, C>> {
Irc::map(self.0.clone(), |inner| &inner.cont)
}
}
impl<C, F, T> IntoFuture for Puck<'_, C, F, T>
where
C: ?Sized + Config,
F: Future,
T: Settle<F::Output>,
{
type Output = F::Output;
type IntoFuture = crate::ops::Join<C, Self>;
#[inline]
fn into_future(self) -> Self::IntoFuture {
crate::ops::join(self)
}
}
union Inner<F: Future, T> {
future: (ManuallyDrop<F>, ManuallyDrop<T>),
result: ManuallyDrop<Option<F::Output>>,
}
impl<F: Future, T> Inner<F, T> {
#[inline]
const fn new(actions: F, settle: T) -> Self {
Inner {
future: (ManuallyDrop::new(actions), ManuallyDrop::new(settle)),
}
}
#[inline]
unsafe fn unchecked_drop(&mut self) {
unsafe {
ManuallyDrop::drop(&mut self.future.0);
ManuallyDrop::drop(&mut self.future.1);
}
}
#[inline]
unsafe fn unchecked_result(&mut self) -> Option<F::Output> {
unsafe { self.result.take() }
}
#[inline]
unsafe fn unchecked_terminate(&mut self, result: F::Output) -> ExitStatus
where
T: Settle<F::Output>,
{
unsafe {
let mut guard = scopeguard::guard((self, result), |(this, result)| {
this.result = ManuallyDrop::new(Some(result));
});
let term = ManuallyDrop::take(&mut guard.0.future.1);
ManuallyDrop::drop(&mut guard.0.future.0);
term.settle(&mut guard.1)
}
}
}
pub trait Settle<R> {
fn settle(self, result: &mut R) -> ExitStatus;
}
impl<R, F> Settle<R> for F
where
F: FnOnce(&mut R) -> ExitStatus,
{
fn settle(self, result: &mut R) -> ExitStatus {
self(result)
}
}
pub struct Unchecked;
impl<R> Settle<R> for Unchecked {
fn settle(self, _result: &mut R) -> ExitStatus {
Ok(crate::Success)
}
}
pub struct Checked;
impl Settle<bool> for Checked {
fn settle(self, result: &mut bool) -> ExitStatus {
if *result {
Ok(crate::Success)
} else {
Err(crate::Failure)
}
}
}
impl<R, T> Settle<Result<R, T>> for Checked {
fn settle(self, result: &mut Result<R, T>) -> ExitStatus {
if result.is_ok() {
Ok(crate::Success)
} else {
Err(crate::Failure)
}
}
}
impl<T> Settle<Option<T>> for Checked {
fn settle(self, result: &mut Option<T>) -> ExitStatus {
if result.is_some() {
Ok(crate::Success)
} else {
Err(crate::Failure)
}
}
}
pub struct Builder<const R: bool = false, F = (), S = Unchecked> {
future: F,
location: Option<&'static Location<'static>>,
finalizer: S,
precedence: Prec,
}
impl Builder {
pub const fn new() -> Self {
Builder {
future: (),
location: None,
finalizer: Unchecked,
precedence: Prec::new(),
}
}
pub const fn root() -> Builder<true> {
Builder {
future: (),
location: None,
finalizer: Unchecked,
precedence: Prec::new(),
}
}
}
impl<const R: bool, S> Builder<R, (), S> {
#[track_caller]
pub fn with_actions<F: IntoFuture>(self, future: F) -> Builder<R, F, S> {
let Builder {
location,
finalizer,
precedence,
..
} = self;
Builder {
future,
location: Some(location.unwrap_or(Location::caller())),
finalizer,
precedence,
}
}
}
impl<const R: bool, F> Builder<R, F> {
pub fn with_finalizer<S>(self, finalizer: S) -> Builder<R, F, S> {
let Builder {
future,
location,
precedence,
..
} = self;
Builder {
future,
location,
finalizer,
precedence,
}
}
pub fn checked(self) -> Builder<R, F, Checked> {
self.with_finalizer(Checked)
}
}
impl<const R: bool, F, S> Builder<R, F, S> {
pub const fn with_precedence(mut self, precedence: Prec) -> Self {
self.precedence = precedence;
self
}
pub const fn with_location(mut self, location: &'static Location<'static>) -> Self {
self.location = Some(location);
self
}
}
impl<const R: bool, F: IntoFuture, S: Settle<F::Output>> Builder<R, F, S> {
pub fn finish<'p, C>(self) -> Lease<'p, Job<'static, C, F::IntoFuture, S>>
where
C: ?Sized + Config,
{
let location = self.location.unwrap();
#[cfg(feature = "tracing")]
let _span = if R {
tracing::Span::current()
} else {
tracing::error_span!("Job", line = location.line()).or_current()
}
.entered();
Lease::new(Job {
cont: Continuation::new(self.precedence, location),
state: RefCell::new(Inner::new(self.future.into_future(), self.finalizer)),
})
}
}
impl Default for Builder {
fn default() -> Self {
Self::new()
}
}
type PhantomDrop<T> = PhantomData<PhantomDropInner<T>>;
struct PhantomDropInner<T: ?Sized>(T);
impl<T: ?Sized> Drop for PhantomDropInner<T> {
fn drop(&mut self) {}
}