use std::fmt::Debug;
use std::future::Future;
use std::pin::Pin;
use acton_ern::Ern;
use futures::future::join_all;
use tracing::{error, trace};
use crate::actor::{ActorConfig, Idle, ManagedActor};
use crate::common::acton_inner::ActonInner;
use crate::common::{ActorHandle, BrokerRef};
use crate::traits::ActorHandleInterface;
#[derive(Debug, Clone, Default)]
pub struct ActorRuntime(pub(crate) ActonInner);
impl ActorRuntime {
pub fn new_actor_with_name<State>(&mut self, name: String) -> ManagedActor<Idle, State>
where
State: Default + Send + Debug + 'static,
{
let actor_config = ActorConfig::new(
Ern::with_root(name).expect("Failed to create root Ern for new actor"), None, Some(self.0.broker.clone()), )
.expect("Failed to create actor config");
let runtime = self.clone();
let new_actor = ManagedActor::new(Some(&runtime), Some(&actor_config));
trace!("Registering new top-level actor: {}", new_actor.id());
self.0
.roots
.insert(new_actor.id.clone(), new_actor.handle.clone());
new_actor
}
pub fn new_actor<State>(&mut self) -> ManagedActor<Idle, State>
where
State: Default + Send + Debug + 'static,
{
self.new_actor_with_name("actor".to_string()) }
#[inline]
#[must_use]
pub fn actor_count(&self) -> usize {
self.0.roots.len()
}
pub fn new_actor_with_config<State>(
&mut self,
mut config: ActorConfig,
) -> ManagedActor<Idle, State>
where
State: Default + Send + Debug + 'static,
{
let acton_ready = self.clone();
if config.broker.is_none() {
config.broker = Some(self.0.broker.clone());
}
let new_actor = ManagedActor::new(Some(&acton_ready), Some(&config));
trace!(
"Created new actor builder with config, id: {}",
new_actor.id()
);
self.0
.roots
.insert(new_actor.id.clone(), new_actor.handle.clone());
new_actor
}
#[inline]
#[must_use]
pub fn broker(&self) -> BrokerRef {
self.0.broker.clone()
}
#[cfg(feature = "ipc")]
#[inline]
#[must_use]
pub fn ipc_registry(&self) -> std::sync::Arc<crate::common::ipc::IpcTypeRegistry> {
self.0.ipc_type_registry.clone()
}
#[cfg(feature = "ipc")]
pub fn ipc_expose(&self, name: &str, handle: ActorHandle) {
trace!("Exposing actor {} for IPC as '{}'", handle.id(), name);
self.0.ipc_actor_registry.insert(name.to_string(), handle);
}
#[cfg(feature = "ipc")]
pub fn ipc_hide(&self, name: &str) -> Option<ActorHandle> {
trace!("Hiding actor '{}' from IPC", name);
self.0.ipc_actor_registry.remove(name).map(|(_, h)| h)
}
#[cfg(feature = "ipc")]
#[must_use]
pub fn ipc_lookup(&self, name: &str) -> Option<ActorHandle> {
self.0.ipc_actor_registry.get(name).map(|r| r.clone())
}
#[cfg(feature = "ipc")]
#[inline]
#[must_use]
pub fn ipc_actor_count(&self) -> usize {
self.0.ipc_actor_registry.len()
}
#[cfg(feature = "ipc")]
pub async fn start_ipc_listener(
&self,
) -> Result<crate::common::ipc::IpcListenerHandle, crate::common::ipc::IpcError> {
let config = crate::common::ipc::IpcConfig::load();
self.start_ipc_listener_with_config(config).await
}
#[cfg(feature = "ipc")]
pub async fn start_ipc_listener_with_config(
&self,
config: crate::common::ipc::IpcConfig,
) -> Result<crate::common::ipc::IpcListenerHandle, crate::common::ipc::IpcError> {
trace!("Starting IPC listener with config: {:?}", config);
let handle = crate::common::ipc::start_listener(
config,
self.0.ipc_type_registry.clone(),
self.0.ipc_actor_registry.clone(),
self.0.cancellation_token.clone(),
)
.await?;
{
let mut guard = self.0.ipc_subscription_manager.write();
*guard = Some(handle.subscription_manager().clone());
}
Ok(handle)
}
pub async fn spawn_actor_with_setup_fn<State>(
&mut self,
mut config: ActorConfig,
setup_fn: impl FnOnce(
ManagedActor<Idle, State>,
) -> Pin<Box<dyn Future<Output = ActorHandle> + Send + 'static>>,
) -> anyhow::Result<ActorHandle>
where
State: Default + Send + Debug + 'static,
{
let acton_ready = self.clone();
if config.broker.is_none() {
config.broker = Some(self.0.broker.clone());
}
let new_actor = ManagedActor::new(Some(&acton_ready), Some(&config));
let actor_id = new_actor.id().clone(); trace!("Running setup function for actor: {}", actor_id);
let handle = setup_fn(new_actor).await; trace!("Actor {} setup complete, registering handle.", actor_id);
self.0.roots.insert(handle.id.clone(), handle.clone()); Ok(handle)
}
pub async fn shutdown_all(&mut self) -> anyhow::Result<()> {
use std::time::Duration;
use tokio::time::timeout as tokio_timeout;
trace!("Sending Terminate signal to all root actors.");
let stop_futures: Vec<_> = self
.0
.roots
.iter()
.map(|item| {
let handle = item.value().clone();
async move {
if let Err(e) = handle.stop().await {
error!("Error stopping actor {}: {:?}", handle.id(), e);
}
}
})
.collect();
let timeout_ms: u64 = self
.0
.config
.system_shutdown_timeout()
.as_millis()
.try_into()
.unwrap_or(u64::MAX);
trace!("Waiting for all actors to finish gracefully...");
if tokio_timeout(Duration::from_millis(timeout_ms), join_all(stop_futures))
.await
.is_err()
{
error!("System-wide shutdown timeout expired after {} ms. Forcefully cancelling remaining tasks.", timeout_ms);
self.0.cancellation_token.cancel(); } else {
trace!("All actors completed gracefully.");
}
trace!("Stopping the system broker...");
if let Ok(res) =
tokio_timeout(Duration::from_millis(timeout_ms), self.0.broker.stop()).await
{
res?;
} else {
error!(
"Timeout waiting for broker to shut down after {} ms",
timeout_ms
);
return Err(anyhow::anyhow!(
"Timeout while waiting for system broker to shut down after {timeout_ms} ms"
));
}
trace!("System shutdown complete.");
Ok(())
}
pub async fn spawn_actor<State>(
&mut self,
setup_fn: impl FnOnce(
ManagedActor<Idle, State>,
) -> Pin<Box<dyn Future<Output = ActorHandle> + Send + 'static>>,
) -> anyhow::Result<ActorHandle>
where
State: Default + Send + Debug + 'static,
{
let config = ActorConfig::new(Ern::default(), None, Some(self.broker()))?;
self.spawn_actor_with_setup_fn(config, setup_fn).await
}
}