use core::{
any::{Any, type_name},
future::{Future, IntoFuture, Pending, pending},
marker::PhantomData,
ops::{Deref, DerefMut},
panic::Location,
pin::Pin,
task::{Context, Poll},
};
use crate::{
Active, Dispatch, ExitStatus,
config::{Config, DefaultConfig},
continuation::{Continuation, Label, Share, erased::State as ContState},
error,
job::{Builder as JobBuilder, Checked, Job, Puck as JobPuck, Settle, Unchecked},
ptr::{AsIrc, Irc, Lease, LeasedMut},
simulator::{Prec, Sim},
};
pub trait Behavior<C: ?Sized + Config = DefaultConfig> {
type Output;
#[allow(async_fn_in_trait)]
async fn actions(&self, sim: &Sim<C>) -> Self::Output;
fn name(&self) -> &'static str {
type_name::<Self>()
}
}
pub trait Actions {
type Config: Config;
type Subject: Any;
type Output;
type Settle: Settle<Self::Output>;
type Action: for<'p> Lifecycle<'p, Self::Config, Self::Subject, Output = Self::Output>;
}
pub trait Lifecycle<'p, C: Config, I: Any + ?Sized> {
type Output;
type Future: Future<Output = Self::Output> + 'p;
fn init(self, item: &'p I, sim: &'p Sim<C>) -> Self::Future;
}
#[pin_project::pin_project]
pub struct Agent<A: Actions> {
#[pin]
inner: Inner<A>,
#[pin]
item: A::Subject,
}
#[pin_project::pin_project(
project = StateProject,
project_ref = StateProjectRef,
project_replace = StateOwn
)]
enum Inner<A: Actions> {
Born {
action: A::Action,
name: &'static str,
rank: Option<<A::Config as Config>::Rank>,
builder: JobBuilder<true, (), A::Settle>,
},
Bust,
Live {
#[pin]
job: RootJob<'static, A>,
#[pin]
share: Share<A::Config>,
},
}
type RootFuture<'l, A> = <<A as Actions>::Action as Lifecycle<
'l,
<A as Actions>::Config,
<A as Actions>::Subject,
>>::Future;
type RootJob<'l, A> =
Job<'static, <A as Actions>::Config, RootFuture<'l, A>, <A as Actions>::Settle>;
type RootPuck<'l, A> =
JobPuck<'l, <A as Actions>::Config, RootFuture<'l, A>, <A as Actions>::Settle>;
impl Agent<()> {
#[track_caller]
pub fn new<'p, C, I>(
subject: I,
) -> Lease<
'p,
Agent<
impl Actions<Config = C, Subject = I, Output = I::Output, Settle = Unchecked> + use<C, I>,
>,
>
where
C: Config,
I: Any + Behavior<C>,
{
Self::build()
.with_name(subject.name())
.with_subject(subject)
.with_actions(I::actions)
.finish()
}
pub const fn build<C: Config>() -> Builder<C> {
Builder::new()
}
}
impl<A: Actions> Deref for Agent<A> {
type Target = A::Subject;
fn deref(&self) -> &Self::Target {
&self.item
}
}
impl<A: Actions> DerefMut for Agent<A> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.item
}
}
impl<A: Actions> Dispatch for Agent<A> {
fn poll(self: Pin<&Self>, cx: &mut Context<'_>) -> Poll<ExitStatus> {
match self.project_ref().inner.project_ref() {
StateProjectRef::Live { job, .. } => job.poll(cx),
_ => unreachable!("the agent should always be active"),
}
}
}
impl<A: Actions> Active<A::Config> for Agent<A> {
type Output = A::Output;
type Puck<'p>
= Puck<'p, A>
where
Self: 'p;
fn bind<'p>(this: Pin<LeasedMut<'p, Self>>, sctx: &'p Share<A::Config>) -> Self::Puck<'p> {
let mut this = this.project().project();
match this.inner.as_mut().project_replace(Inner::Bust) {
StateOwn::Born {
action,
name,
rank,
builder,
} => {
use core::mem::transmute;
let item = this.item.into_ref().get_ref();
let sim = sctx.sim();
let pid = sim.pid_gen::<A::Subject>();
#[cfg(feature = "tracing")]
let _span = tracing::error_span!(
parent: None, "Agent",
label = %Label { name, pid: Some(pid) }
)
.entered();
let live = builder.with_actions(action.init(item, sim)).finish();
this.inner.set(Inner::Live {
job: unsafe {
transmute::<RootJob<'p, A>, RootJob<'static, A>>(live.into_inner())
},
share: unsafe {
Share::new(
sim.clone(),
item,
rank.unwrap_or(sim.config().default_rank()),
name,
pid,
)
},
});
match this.inner.project() {
StateProject::Live { job, share } => {
let live = unsafe {
transmute::<
Pin<&'p mut RootJob<'static, A>>,
Pin<LeasedMut<'p, RootJob<'p, A>>>,
>(job)
};
Puck(Active::bind(live, share.into_ref().get_ref()))
}
_ => unreachable!(),
}
}
_ => unreachable!("the agent has already been bound, which should not be possible"),
}
}
}
pub struct Puck<'p, A: Actions>(RootPuck<'p, A>);
impl<C: Config, A: Actions<Config = C>> Puck<'_, A> {
pub fn update_rank(&self, rank: C::Rank) {
self.0.share().update_rank(rank);
}
pub fn subject(&self) -> &A::Subject {
use crate::Puck;
self.0.subject().downcast_ref::<A::Subject>().unwrap()
}
pub fn abort(self) {
self.0.abort();
}
}
impl<C: Config, A: Actions<Config = C>> crate::Puck<C> for Puck<'_, A> {
fn result(&mut self) -> Option<Self::Output> {
self.0.result()
}
fn wake(&mut self) -> Result<(), error::NotIdle> {
self.0.wake()
}
fn subject(&self) -> &dyn Any {
self.0.subject()
}
fn sim(&self) -> &Sim<C> {
self.0.sim()
}
fn label(&self) -> Label {
self.0.label()
}
fn time(&self) -> Option<C::Time> {
self.0.time()
}
fn rank(&self) -> C::Rank {
self.0.rank()
}
fn prec(&self) -> Prec {
self.0.prec()
}
fn state(&self) -> ContState {
self.0.state()
}
fn location(&self) -> &'static Location<'static> {
self.0.location()
}
}
impl<A: Actions> AsRef<Continuation<'static, A::Config>> for Puck<'_, A> {
fn as_ref(&self) -> &Continuation<'static, A::Config> {
self.0.as_ref()
}
}
impl<A: Actions> AsIrc<Continuation<'static, A::Config>> for Puck<'_, A> {
fn as_irc(&self) -> Irc<Continuation<'static, A::Config>> {
self.0.as_irc()
}
}
impl<A: Actions> IntoFuture for Puck<'_, A> {
type Output = A::Output;
type IntoFuture = crate::ops::Join<A::Config, Self>;
fn into_future(self) -> Self::IntoFuture {
crate::ops::join(self)
}
}
pub struct Builder<C: Config, I = (), A = (), S = Unchecked> {
subject: I,
actions: A,
name: Option<&'static str>,
rank: Option<C::Rank>,
settle: S,
location: Option<&'static Location<'static>>,
_config: PhantomData<C>,
}
impl<C: Config> Builder<C, (), ()> {
const fn new() -> Self {
Builder {
subject: (),
actions: (),
name: None,
rank: None,
settle: Unchecked,
location: None,
_config: PhantomData,
}
}
}
impl<C: Config, I: Any, A, S> Builder<C, I, A, S> {
pub fn with_subject<X: Any>(self, object: X) -> Builder<C, X, A, S> {
Builder {
subject: object,
actions: self.actions,
name: self.name,
rank: self.rank,
settle: self.settle,
location: self.location,
_config: self._config,
}
}
#[track_caller]
pub fn with_actions<X>(self, actions: X) -> Builder<C, I, X, S>
where
X: for<'l> Lifecycle<'l, C, I>,
{
Builder {
subject: self.subject,
actions,
name: self.name,
rank: self.rank,
settle: self.settle,
location: Some(self.location.unwrap_or_else(Location::caller)),
_config: self._config,
}
}
pub const fn with_location(mut self, location: &'static Location<'static>) -> Self {
self.location = Some(location);
self
}
pub const fn with_name(mut self, name: &'static str) -> Self {
self.name = Some(name);
self
}
pub fn with_rank(self, rank: C::Rank) -> Self {
Builder {
rank: Some(rank),
..self
}
}
pub fn with_finalizer<X>(self, finalizer: X) -> Builder<C, I, A, X> {
Builder {
subject: self.subject,
actions: self.actions,
name: self.name,
rank: self.rank,
settle: finalizer,
location: self.location,
_config: self._config,
}
}
pub fn checked(self) -> Builder<C, I, A, Checked> {
self.with_finalizer(Checked)
}
}
impl<C: Config, I: Any, R, A, S> Builder<C, I, A, S>
where
A: for<'l> Lifecycle<'l, C, I, Output = R>,
S: Settle<R>,
{
pub fn finish<'p>(self) -> Lease<'p, Agent<Action<C, I, A, S>>> {
Lease::new(Agent {
item: self.subject,
inner: Inner::Born {
action: self.actions,
name: match self.name {
Some(name) => name,
_ => type_name::<I>(),
},
rank: self.rank,
builder: JobBuilder::root()
.with_finalizer(self.settle)
.with_location(self.location.unwrap()),
},
})
}
}
impl<C, I, A, R> Behavior<C> for (I, A)
where
C: Config,
I: Any,
A: Copy + for<'l> Lifecycle<'l, C, I, Output = R>,
{
type Output = R;
async fn actions(&self, sim: &Sim<C>) -> R {
self.1.init(&self.0, sim).await
}
fn name(&self) -> &'static str {
type_name::<I>()
}
}
pub struct Action<C, I, A, T>(PhantomData<(C, I, A, T)>);
impl<C: Config, I: Any, R, A, T: Settle<R>> Actions for Action<C, I, A, T>
where
A: for<'l> Lifecycle<'l, C, I, Output = R>,
{
type Config = C;
type Subject = I;
type Output = R;
type Settle = T;
type Action = A;
}
impl Actions for () {
type Config = DefaultConfig;
type Subject = ();
type Output = ();
type Settle = Unchecked;
type Action = Pending<()>;
}
impl<'p, C, I> Lifecycle<'p, C, I> for Pending<()>
where
C: Config,
I: Any,
{
type Output = ();
type Future = Pending<()>;
fn init(self, _item: &'p I, _sim: &'p Sim<C>) -> Self::Future {
pending()
}
}
impl<'p, C, I, A, F> Lifecycle<'p, C, I> for A
where
C: Config,
I: Any,
A: FnOnce(&'p I, &'p Sim<C>) -> F,
F: Future + 'p,
{
type Output = F::Output;
type Future = F;
fn init(self, item: &'p I, sim: &'p Sim<C>) -> Self::Future {
self(item, sim)
}
}