|
|
|
@ -16,12 +16,14 @@ use preserves::value::Domain;
|
|
|
|
|
use preserves::value::IOValue;
|
|
|
|
|
use preserves::value::Map;
|
|
|
|
|
use preserves::value::NestedValue;
|
|
|
|
|
use preserves::value::Set;
|
|
|
|
|
use preserves_schema::support::ParseError;
|
|
|
|
|
|
|
|
|
|
use std::boxed::Box;
|
|
|
|
|
use std::collections::hash_map::HashMap;
|
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
|
use std::convert::TryInto;
|
|
|
|
|
use std::num::NonZeroU64;
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
use std::sync::Mutex;
|
|
|
|
|
use std::sync::RwLock;
|
|
|
|
@ -50,6 +52,9 @@ pub type AnyValue = super::schemas::internal_protocol::_Any;
|
|
|
|
|
/// The type of process-unique actor IDs.
|
|
|
|
|
pub type ActorId = u64;
|
|
|
|
|
|
|
|
|
|
/// The type of process-unique facet IDs.
|
|
|
|
|
pub type FacetId = NonZeroU64;
|
|
|
|
|
|
|
|
|
|
/// The type of process-unique assertion handles.
|
|
|
|
|
///
|
|
|
|
|
/// Used both as a reference to [retract][Entity::retract]
|
|
|
|
@ -60,8 +65,8 @@ pub type Handle = u64;
|
|
|
|
|
/// Responses to events must have type `ActorResult`.
|
|
|
|
|
pub type ActorResult = Result<(), Error>;
|
|
|
|
|
|
|
|
|
|
/// Methods [`Actor::boot`] and [`Actor::start`] return an
|
|
|
|
|
/// `ActorHandle`, representing the actor's mainloop task.
|
|
|
|
|
/// The [`Actor::boot`] method returns an `ActorHandle`, representing
|
|
|
|
|
/// the actor's mainloop task.
|
|
|
|
|
pub type ActorHandle = tokio::task::JoinHandle<ActorResult>;
|
|
|
|
|
|
|
|
|
|
/// A small protocol for indicating successful synchronisation with
|
|
|
|
@ -193,17 +198,22 @@ pub type PendingEventQueue = Vec<Action>;
|
|
|
|
|
/// The main API for programming Syndicated Actor objects.
|
|
|
|
|
///
|
|
|
|
|
/// Through `Activation`s, programs can access the state of their
|
|
|
|
|
/// animating [`RunningActor`].
|
|
|
|
|
/// animating [`RunningActor`] and their active [`Facet`].
|
|
|
|
|
///
|
|
|
|
|
/// Usually, an `Activation` will be supplied to code that needs one; but when non-Actor code
|
|
|
|
|
/// (such as a [linked task][crate::actor#linked-tasks]) needs to enter an Actor's execution
|
|
|
|
|
/// context, use [`FacetRef::activate`] to construct one.
|
|
|
|
|
///
|
|
|
|
|
/// Many actions that an entity can perform are methods directly on
|
|
|
|
|
/// `Activation`, but methods on the [`RunningActor`] and [`ActorRef`]
|
|
|
|
|
/// `Activation`, but methods on the [`RunningActor`] and [`FacetRef`]
|
|
|
|
|
/// values contained in an `Activation` are also sometimes useful.
|
|
|
|
|
///
|
|
|
|
|
/// This is what other implementations call a "Turn", renamed here to
|
|
|
|
|
/// avoid conflicts with [`crate::schemas::internal_protocol::Turn`].
|
|
|
|
|
pub struct Activation<'activation> {
|
|
|
|
|
/// A reference to the implementation-side of the currently active [`Actor`].
|
|
|
|
|
pub actor: ActorRef,
|
|
|
|
|
/// A reference to the currently active [`Facet`] and the implementation-side state of its
|
|
|
|
|
/// [`Actor`].
|
|
|
|
|
pub facet: FacetRef,
|
|
|
|
|
/// A reference to the current state of the active [`Actor`].
|
|
|
|
|
pub state: &'activation mut RunningActor,
|
|
|
|
|
pending: EventBuffer,
|
|
|
|
@ -272,6 +282,14 @@ pub struct ActorRef {
|
|
|
|
|
state: Arc<Mutex<ActorState>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A combination of an [`ActorRef`] with a [`FacetId`], acting as a capability to enter the
|
|
|
|
|
/// execution context of a facet from a linked task.
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct FacetRef {
|
|
|
|
|
pub actor: ActorRef,
|
|
|
|
|
pub facet_id: FacetId,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The state of an actor: either `Running` or `Terminated`.
|
|
|
|
|
pub enum ActorState {
|
|
|
|
|
/// A non-terminated actor has an associated [`RunningActor`] state record.
|
|
|
|
@ -290,9 +308,40 @@ pub struct RunningActor {
|
|
|
|
|
pub actor_id: ActorId,
|
|
|
|
|
tx: UnboundedSender<SystemMessage>,
|
|
|
|
|
mailbox: Weak<Mailbox>,
|
|
|
|
|
exit_hooks: Vec<Box<dyn Send + FnOnce(&mut Activation, &Arc<ActorResult>) -> ActorResult>>,
|
|
|
|
|
facet_nodes: Map<FacetId, Facet>,
|
|
|
|
|
facet_children: Map<FacetId, Set<FacetId>>,
|
|
|
|
|
root: FacetId,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// State associated with each facet in an [`Actor`]'s facet tree.
|
|
|
|
|
///
|
|
|
|
|
/// # Inert facets
|
|
|
|
|
///
|
|
|
|
|
/// A facet is considered *inert* if:
|
|
|
|
|
///
|
|
|
|
|
/// 1. it has no child facets;
|
|
|
|
|
/// 2. it has no cleanup actions (that is, no assertions placed by any of its entities);
|
|
|
|
|
/// 3. it has no linked tasks; and
|
|
|
|
|
/// 4. it has no "inert check preventers" (see [Activation::prevent_inert_check]).
|
|
|
|
|
///
|
|
|
|
|
/// If a facet is created and is inert at the moment that its `boot` function returns, it is
|
|
|
|
|
/// automatically terminated.
|
|
|
|
|
///
|
|
|
|
|
/// When a facet is terminated, if its parent facet is inert, the parent is terminated.
|
|
|
|
|
///
|
|
|
|
|
/// If the root facet in an actor is terminated, the entire actor is terminated (with exit
|
|
|
|
|
/// status `Ok(())`).
|
|
|
|
|
///
|
|
|
|
|
pub struct Facet {
|
|
|
|
|
/// The ID of the facet.
|
|
|
|
|
pub facet_id: FacetId,
|
|
|
|
|
/// The ID of the facet's parent facet, if any; if None, this facet is the `Actor`'s root facet.
|
|
|
|
|
pub parent_facet_id: Option<FacetId>,
|
|
|
|
|
cleanup_actions: CleanupActions,
|
|
|
|
|
stop_actions: Vec<Action>,
|
|
|
|
|
linked_tasks: Map<u64, CancellationToken>,
|
|
|
|
|
exit_hooks: Vec<Box<dyn Send + FnOnce(&mut Activation, &Arc<ActorResult>) -> ActorResult>>,
|
|
|
|
|
inert_check_preventers: Arc<AtomicU64>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A reference to an object that expects messages/assertions of type
|
|
|
|
@ -303,6 +352,8 @@ pub struct RunningActor {
|
|
|
|
|
pub struct Ref<M> {
|
|
|
|
|
/// Mailbox of the actor owning the referenced entity.
|
|
|
|
|
pub mailbox: Arc<Mailbox>,
|
|
|
|
|
/// ID of the facet (within the actor) owning the referenced entity.
|
|
|
|
|
pub facet_id: FacetId,
|
|
|
|
|
/// Mutex owning and guarding the state backing the referenced entity.
|
|
|
|
|
pub target: Mutex<Option<Box<dyn Entity<M>>>>,
|
|
|
|
|
}
|
|
|
|
@ -352,15 +403,22 @@ pub fn next_actor_id() -> ActorId {
|
|
|
|
|
NEXT_ACTOR_ID.fetch_add(BUMP_AMOUNT.into(), Ordering::Relaxed)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NEXT_HANDLE: AtomicU64 = AtomicU64::new(2);
|
|
|
|
|
static NEXT_FACET_ID: AtomicU64 = AtomicU64::new(2);
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
|
pub fn next_facet_id() -> FacetId {
|
|
|
|
|
FacetId::new(NEXT_FACET_ID.fetch_add(BUMP_AMOUNT.into(), Ordering::Relaxed))
|
|
|
|
|
.expect("Internal error: Attempt to allocate FacetId of zero. Too many FacetIds allocated. Restart the process.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NEXT_HANDLE: AtomicU64 = AtomicU64::new(3);
|
|
|
|
|
/// Allocate a process-unique `Handle`.
|
|
|
|
|
pub fn next_handle() -> Handle {
|
|
|
|
|
NEXT_HANDLE.fetch_add(BUMP_AMOUNT.into(), Ordering::Relaxed)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NEXT_ACCOUNT_ID: AtomicU64 = AtomicU64::new(3);
|
|
|
|
|
static NEXT_ACCOUNT_ID: AtomicU64 = AtomicU64::new(4);
|
|
|
|
|
|
|
|
|
|
static NEXT_TASK_ID: AtomicU64 = AtomicU64::new(4);
|
|
|
|
|
static NEXT_TASK_ID: AtomicU64 = AtomicU64::new(5);
|
|
|
|
|
|
|
|
|
|
preserves_schema::support::lazy_static! {
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
@ -394,29 +452,20 @@ impl From<&Synced> for AnyValue {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'activation> Activation<'activation> {
|
|
|
|
|
fn make(actor: &ActorRef, account: Arc<Account>, state: &'activation mut RunningActor) -> Self {
|
|
|
|
|
Activation {
|
|
|
|
|
actor: actor.clone(),
|
|
|
|
|
state,
|
|
|
|
|
pending: EventBuffer::new(account),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Constructs and executes `f` in a new "turn" for `actor`. If
|
|
|
|
|
/// `f` returns `Ok(())`, [commits the turn][Self::deliver] and
|
|
|
|
|
/// performs the buffered actions; otherwise, [abandons the
|
|
|
|
|
/// turn][Self::clear] and discards the buffered actions.
|
|
|
|
|
impl FacetRef {
|
|
|
|
|
/// Executes `f` in a new "[turn][Activation]" for `actor`. If `f` returns `Ok(())`,
|
|
|
|
|
/// [commits the turn][Activation::deliver] and performs the buffered actions; otherwise,
|
|
|
|
|
/// [abandons the turn][Activation::clear] and discards the buffered actions.
|
|
|
|
|
///
|
|
|
|
|
/// Bills any activity to `account`.
|
|
|
|
|
pub fn for_actor<F>(
|
|
|
|
|
actor: &ActorRef,
|
|
|
|
|
pub fn activate<F>(
|
|
|
|
|
&self,
|
|
|
|
|
account: Arc<Account>,
|
|
|
|
|
f: F,
|
|
|
|
|
) -> ActorResult where
|
|
|
|
|
F: FnOnce(&mut Activation) -> ActorResult,
|
|
|
|
|
{
|
|
|
|
|
match Self::for_actor_exit(actor, account, |t| match f(t) {
|
|
|
|
|
match self.activate_exit(account, |t| match f(t) {
|
|
|
|
|
Ok(()) => None,
|
|
|
|
|
Err(e) => Some(Err(e)),
|
|
|
|
|
}) {
|
|
|
|
@ -425,28 +474,26 @@ impl<'activation> Activation<'activation> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Constructs and executes `f` in a new "turn" for `actor`. If
|
|
|
|
|
/// `f` returns `Some(exit_status)`, terminates `actor` with that
|
|
|
|
|
/// `exit_status`. Otherwise, if `f` returns `None`, leaves
|
|
|
|
|
/// `actor` in runnable state. [Commits buffered
|
|
|
|
|
/// actions][Self::deliver] unless `actor` terminates with an
|
|
|
|
|
/// `Err` status.
|
|
|
|
|
/// Executes `f` in a new "[turn][Activation]" for `actor`. If `f` returns
|
|
|
|
|
/// `Some(exit_status)`, terminates `actor` with that `exit_status`. Otherwise, if `f`
|
|
|
|
|
/// returns `None`, leaves `actor` in runnable state. [Commits buffered
|
|
|
|
|
/// actions][Activation::deliver] unless `actor` terminates with an `Err` status.
|
|
|
|
|
///
|
|
|
|
|
/// Bills any activity to `account`.
|
|
|
|
|
pub fn for_actor_exit<F>(
|
|
|
|
|
actor: &ActorRef,
|
|
|
|
|
pub fn activate_exit<F>(
|
|
|
|
|
&self,
|
|
|
|
|
account: Arc<Account>,
|
|
|
|
|
f: F,
|
|
|
|
|
) -> Option<ActorResult> where
|
|
|
|
|
F: FnOnce(&mut Activation) -> Option<ActorResult>,
|
|
|
|
|
{
|
|
|
|
|
match actor.state.lock() {
|
|
|
|
|
match self.actor.state.lock() {
|
|
|
|
|
Err(_) => panicked_err(),
|
|
|
|
|
Ok(mut g) => match &mut *g {
|
|
|
|
|
ActorState::Terminated { exit_status } =>
|
|
|
|
|
Some((**exit_status).clone()),
|
|
|
|
|
ActorState::Running(state) => {
|
|
|
|
|
let mut activation = Activation::make(actor, account, state);
|
|
|
|
|
let mut activation = Activation::make(self, account, state);
|
|
|
|
|
match f(&mut activation) {
|
|
|
|
|
None => None,
|
|
|
|
|
Some(exit_status) => {
|
|
|
|
@ -455,12 +502,19 @@ impl<'activation> Activation<'activation> {
|
|
|
|
|
}
|
|
|
|
|
drop(activation);
|
|
|
|
|
let exit_status = Arc::new(exit_status);
|
|
|
|
|
let mut t = Activation::make(actor, Account::new(crate::name!("shutdown")), state);
|
|
|
|
|
let mut t = Activation::make(&self.actor.facet_ref(state.root),
|
|
|
|
|
Account::new(crate::name!("shutdown")),
|
|
|
|
|
state);
|
|
|
|
|
for action in std::mem::take(&mut t.state.exit_hooks) {
|
|
|
|
|
if let Err(err) = action(&mut t, &exit_status) {
|
|
|
|
|
tracing::error!(err = debug(err), "error in exit hook");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if let Err(err) = t._terminate_facet(t.state.root, false) {
|
|
|
|
|
// This can only occur as the result of an internal error in this file's code.
|
|
|
|
|
tracing::error!(err = debug(err), "unexpected error from disorderly terminate_facet");
|
|
|
|
|
panic!("Unexpected error result from disorderly terminate_facet");
|
|
|
|
|
}
|
|
|
|
|
*g = ActorState::Terminated {
|
|
|
|
|
exit_status: Arc::clone(&exit_status),
|
|
|
|
|
};
|
|
|
|
@ -471,29 +525,72 @@ impl<'activation> Activation<'activation> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'activation> Activation<'activation> {
|
|
|
|
|
fn make(
|
|
|
|
|
facet: &FacetRef,
|
|
|
|
|
account: Arc<Account>,
|
|
|
|
|
state: &'activation mut RunningActor,
|
|
|
|
|
) -> Self {
|
|
|
|
|
Activation {
|
|
|
|
|
facet: facet.clone(),
|
|
|
|
|
state,
|
|
|
|
|
pending: EventBuffer::new(account),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn immediate_oid<M>(&self, r: &Arc<Ref<M>>) {
|
|
|
|
|
if r.mailbox.actor_id != self.actor.actor_id {
|
|
|
|
|
if r.mailbox.actor_id != self.facet.actor.actor_id {
|
|
|
|
|
panic!("Cannot use for_myself to send to remote peers");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn with_facet<F>(&mut self, check_existence: bool, facet_id: FacetId, f: F) -> ActorResult
|
|
|
|
|
where
|
|
|
|
|
F: FnOnce(&mut Activation) -> ActorResult,
|
|
|
|
|
{
|
|
|
|
|
if !check_existence || self.state.facet_nodes.contains_key(&facet_id) {
|
|
|
|
|
let old_facet_id = self.facet.facet_id;
|
|
|
|
|
self.facet.facet_id = facet_id;
|
|
|
|
|
let result = f(self);
|
|
|
|
|
self.facet.facet_id = old_facet_id;
|
|
|
|
|
result
|
|
|
|
|
} else {
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
|
pub fn with_entity<M, F>(&mut self, r: &Arc<Ref<M>>, f: F) -> ActorResult where
|
|
|
|
|
F: FnOnce(&mut Activation, &mut dyn Entity<M>) -> ActorResult
|
|
|
|
|
{
|
|
|
|
|
self.with_facet(true, r.facet_id, |t| r.internal_with_entity(|e| f(t, e)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn active_facet<'a>(&'a mut self) -> Option<&'a mut Facet> {
|
|
|
|
|
self.state.get_facet(self.facet.facet_id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Core API: assert `a` at recipient `r`.
|
|
|
|
|
///
|
|
|
|
|
/// Returns the [`Handle`] for the new assertion.
|
|
|
|
|
pub fn assert<M: 'static + Send>(&mut self, r: &Arc<Ref<M>>, a: M) -> Handle {
|
|
|
|
|
let handle = next_handle();
|
|
|
|
|
{
|
|
|
|
|
let r = Arc::clone(r);
|
|
|
|
|
self.pending.queue_for(&r).push(Box::new(
|
|
|
|
|
move |t| r.with_entity(|e| e.assert(t, a, handle))));
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
let r = Arc::clone(r);
|
|
|
|
|
self.state.cleanup_actions.insert(
|
|
|
|
|
handle,
|
|
|
|
|
CleanupAction::ForAnother(Arc::clone(&r.mailbox), Box::new(
|
|
|
|
|
move |t| r.with_entity(|e| e.retract(t, handle)))));
|
|
|
|
|
if let Some(f) = self.active_facet() {
|
|
|
|
|
{
|
|
|
|
|
let r = Arc::clone(r);
|
|
|
|
|
f.cleanup_actions.insert(
|
|
|
|
|
handle,
|
|
|
|
|
CleanupAction::ForAnother(Arc::clone(&r.mailbox), Box::new(
|
|
|
|
|
move |t| t.with_entity(&r, |t, e| e.retract(t, handle)))));
|
|
|
|
|
}
|
|
|
|
|
drop(f);
|
|
|
|
|
{
|
|
|
|
|
let r = Arc::clone(r);
|
|
|
|
|
self.pending.queue_for(&r).push(Box::new(
|
|
|
|
|
move |t| t.with_entity(&r, |t, e| e.assert(t, a, handle))));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
handle
|
|
|
|
|
}
|
|
|
|
@ -515,25 +612,30 @@ impl<'activation> Activation<'activation> {
|
|
|
|
|
pub fn assert_for_myself<M: 'static + Send>(&mut self, r: &Arc<Ref<M>>, a: M) -> Handle {
|
|
|
|
|
self.immediate_oid(r);
|
|
|
|
|
let handle = next_handle();
|
|
|
|
|
{
|
|
|
|
|
let r = Arc::clone(r);
|
|
|
|
|
self.pending.for_myself.push(Box::new(
|
|
|
|
|
move |t| r.with_entity(|e| e.assert(t, a, handle))));
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
let r = Arc::clone(r);
|
|
|
|
|
self.state.cleanup_actions.insert(
|
|
|
|
|
handle,
|
|
|
|
|
CleanupAction::ForMyself(Box::new(
|
|
|
|
|
move |t| r.with_entity(|e| e.retract(t, handle)))));
|
|
|
|
|
if let Some(f) = self.active_facet() {
|
|
|
|
|
{
|
|
|
|
|
let r = Arc::clone(r);
|
|
|
|
|
f.cleanup_actions.insert(
|
|
|
|
|
handle,
|
|
|
|
|
CleanupAction::ForMyself(Box::new(
|
|
|
|
|
move |t| t.with_entity(&r, |t, e| e.retract(t, handle)))));
|
|
|
|
|
}
|
|
|
|
|
drop(f);
|
|
|
|
|
{
|
|
|
|
|
let r = Arc::clone(r);
|
|
|
|
|
self.pending.for_myself.push(Box::new(
|
|
|
|
|
move |t| t.with_entity(&r, |t, e| e.assert(t, a, handle))));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
handle
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Core API: retract a previously-established assertion.
|
|
|
|
|
pub fn retract(&mut self, handle: Handle) {
|
|
|
|
|
if let Some(d) = self.state.cleanup_actions.remove(&handle) {
|
|
|
|
|
self.pending.execute_cleanup_action(d)
|
|
|
|
|
if let Some(f) = self.active_facet() {
|
|
|
|
|
if let Some(d) = f.cleanup_actions.remove(&handle) {
|
|
|
|
|
self.pending.execute_cleanup_action(d)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -541,7 +643,7 @@ impl<'activation> Activation<'activation> {
|
|
|
|
|
pub fn message<M: 'static + Send>(&mut self, r: &Arc<Ref<M>>, m: M) {
|
|
|
|
|
let r = Arc::clone(r);
|
|
|
|
|
self.pending.queue_for(&r).push(Box::new(
|
|
|
|
|
move |t| r.with_entity(|e| e.message(t, m))))
|
|
|
|
|
move |t| t.with_entity(&r, |t, e| e.message(t, m))))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Core API: send message `m` to recipient `r`, which must be a
|
|
|
|
@ -558,7 +660,7 @@ impl<'activation> Activation<'activation> {
|
|
|
|
|
self.immediate_oid(r);
|
|
|
|
|
let r = Arc::clone(r);
|
|
|
|
|
self.pending.for_myself.push(Box::new(
|
|
|
|
|
move |t| r.with_entity(|e| e.message(t, m))))
|
|
|
|
|
move |t| t.with_entity(&r, |t, e| e.message(t, m))))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Core API: begins a synchronisation with `r`.
|
|
|
|
@ -569,7 +671,7 @@ impl<'activation> Activation<'activation> {
|
|
|
|
|
pub fn sync<M: 'static + Send>(&mut self, r: &Arc<Ref<M>>, peer: Arc<Ref<Synced>>) {
|
|
|
|
|
let r = Arc::clone(r);
|
|
|
|
|
self.pending.queue_for(&r).push(Box::new(
|
|
|
|
|
move |t| r.with_entity(|e| e.sync(t, peer))))
|
|
|
|
|
move |t| t.with_entity(&r, |t, e| e.sync(t, peer))))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Retrieve the [`Account`] against which actions are recorded.
|
|
|
|
@ -596,6 +698,182 @@ impl<'activation> Activation<'activation> {
|
|
|
|
|
pub fn deliver(&mut self) {
|
|
|
|
|
self.pending.deliver();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Construct an entity with behaviour [`InertEntity`] within the active facet.
|
|
|
|
|
pub fn inert_entity<M>(&mut self) -> Arc<Ref<M>> {
|
|
|
|
|
self.create(InertEntity)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Construct an entity with behaviour `e` within the active facet.
|
|
|
|
|
pub fn create<M, E: Entity<M> + Send + 'static>(&mut self, e: E) -> Arc<Ref<M>> {
|
|
|
|
|
let r = self.create_inert();
|
|
|
|
|
r.become_entity(e);
|
|
|
|
|
r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Construct an entity (within the active facet) whose behaviour will be specified later
|
|
|
|
|
/// via [`become_entity`][Ref::become_entity].
|
|
|
|
|
pub fn create_inert<M>(&mut self) -> Arc<Ref<M>> {
|
|
|
|
|
Arc::new(Ref {
|
|
|
|
|
mailbox: self.state.mailbox(),
|
|
|
|
|
facet_id: self.facet.facet_id,
|
|
|
|
|
target: Mutex::new(None),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Start a new [linked task][crate::actor#linked-tasks] attached to the active facet. The
|
|
|
|
|
/// task will execute the future "`boot`" to completion unless it is cancelled first (by
|
|
|
|
|
/// e.g. termination of the owning facet or crashing of the owning actor). Uses `name` for
|
|
|
|
|
/// log messages emitted by the task.
|
|
|
|
|
pub fn linked_task<F: 'static + Send + futures::Future<Output = ActorResult>>(
|
|
|
|
|
&mut self,
|
|
|
|
|
name: tracing::Span,
|
|
|
|
|
boot: F,
|
|
|
|
|
) {
|
|
|
|
|
let mailbox = self.state.mailbox();
|
|
|
|
|
if let Some(f) = self.active_facet() {
|
|
|
|
|
let token = CancellationToken::new();
|
|
|
|
|
let task_id = NEXT_TASK_ID.fetch_add(1, Ordering::Relaxed);
|
|
|
|
|
name.record("task_id", &task_id);
|
|
|
|
|
{
|
|
|
|
|
let token = token.clone();
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
tracing::trace!(task_id, "linked task start");
|
|
|
|
|
select! {
|
|
|
|
|
_ = token.cancelled() => {
|
|
|
|
|
tracing::trace!(task_id, "linked task cancelled");
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
result = boot => {
|
|
|
|
|
match &result {
|
|
|
|
|
Ok(()) => {
|
|
|
|
|
tracing::trace!(task_id, "linked task normal stop");
|
|
|
|
|
()
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
tracing::error!(task_id, "linked task error: {}", e);
|
|
|
|
|
let _ = mailbox.tx.send(SystemMessage::Crash(e.clone()));
|
|
|
|
|
()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}.instrument(name));
|
|
|
|
|
}
|
|
|
|
|
f.linked_tasks.insert(task_id, token);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create a new subfacet of the currently-active facet. Runs `boot` in the new facet's
|
|
|
|
|
/// context. If `boot` returns leaving the new facet [inert][Facet#inert-facets],
|
|
|
|
|
pub fn facet<F: 'static + Send + FnOnce(&mut Activation) -> ActorResult>(
|
|
|
|
|
&mut self,
|
|
|
|
|
boot: F,
|
|
|
|
|
) -> Result<FacetId, Error> {
|
|
|
|
|
let f = Facet::new(Some(self.facet.facet_id));
|
|
|
|
|
let facet_id = f.facet_id;
|
|
|
|
|
self.state.facet_nodes.insert(facet_id, f);
|
|
|
|
|
self.state.facet_children.entry(self.facet.facet_id).or_default().insert(facet_id);
|
|
|
|