use crate::{
Actor, ActorConfig, ActorId, ActorRef,
actor_context::spawn,
mailbox::{ActorTerminated, TerminatedSink, Watcher},
sync::lock,
};
use derive_more::Debug;
use std::sync::{Arc, Mutex};
use thiserror::Error;
use tokio::sync::{oneshot, watch};
#[must_use = "dropping an actor system does not stop its actors"]
#[derive(Debug)]
pub struct ActorSystem<M> {
root: ActorRef<M>,
#[debug(skip)]
terminated_rx: oneshot::Receiver<()>,
}
impl<M> ActorSystem<M>
where
M: Send + 'static,
{
pub fn new<A>(actor: A) -> Self
where
A: Actor<Message = M> + Send + 'static,
A::State: Send + 'static,
{
Self::with_config(actor, ActorConfig::default())
}
pub fn with_config<A>(actor: A, config: ActorConfig) -> Self
where
A: Actor<Message = M> + Send + 'static,
A::State: Send + 'static,
{
let (root, terminated_rx) = spawn_root(actor, config);
Self::from_parts(root, terminated_rx)
}
pub fn root(&self) -> &ActorRef<M> {
&self.root
}
pub async fn terminated(self) -> Result<(), Error> {
self.terminated_rx.await?;
Ok(())
}
pub(crate) fn from_parts(root: ActorRef<M>, terminated_rx: oneshot::Receiver<()>) -> Self {
Self {
root,
terminated_rx,
}
}
}
#[derive(Debug, Error)]
pub enum Error {
#[error("root watch failed unexpectedly")]
WatchRoot(#[from] oneshot::error::RecvError),
}
pub(crate) fn watch_root<M>(
root: &ActorRef<M>,
stopping_tx: watch::Sender<()>,
) -> oneshot::Receiver<()> {
let (terminated_tx, terminated_rx) = oneshot::channel();
let sink = Arc::new(RootTerminatedSink {
terminated_tx: Mutex::new(Some(terminated_tx)),
_stopping_tx: stopping_tx,
});
let registration = root
.watcher_registry()
.add(Watcher::new(ActorId::new(), sink.clone()));
if registration.is_err() {
sink.send_terminated(root.actor_id())
.expect("a sink whose registration failed was never signaled");
}
terminated_rx
}
struct RootTerminatedSink {
terminated_tx: Mutex<Option<oneshot::Sender<()>>>,
_stopping_tx: watch::Sender<()>,
}
impl TerminatedSink for RootTerminatedSink {
fn send_terminated(&self, _actor_id: ActorId) -> Result<(), ActorTerminated> {
let terminated_tx = lock(&self.terminated_tx).take().ok_or(ActorTerminated)?;
let _ = terminated_tx.send(());
Ok(())
}
}
fn spawn_root<M, A>(root_actor: A, config: ActorConfig) -> (ActorRef<M>, oneshot::Receiver<()>)
where
M: Send + 'static,
A: Actor<Message = M> + Send + 'static,
A::State: Send + 'static,
{
let (stopping_tx, stopping_rx) = watch::channel(());
let root = spawn(stopping_rx, root_actor, config);
let terminated_rx = watch_root(&root, stopping_tx);
(root, terminated_rx)
}