Skip to main content

acktor_ipc/
node.rs

1//! Node actor for managing IPC connections and sessions.
2//!
3
4use std::marker::PhantomData;
5use std::sync::Arc;
6
7use ahash::{HashMap, HashSet};
8use futures_util::future::join_all;
9use tracing::{error, info, warn};
10
11use acktor::{
12    Actor, ActorContext, ActorId, Address, ErrorReport, Handler, JoinHandle, Recipient, Signal,
13    message::FutureMessageResult,
14    observer::{ObserverSet, SubjectActor},
15    supervisor::SupervisionEvent,
16    utils::{ShortName, debug_trace, terminate_actor},
17};
18
19use crate::actor_ref::ActorRef;
20use crate::double_map::DoubleMap;
21use crate::error::NodeError;
22use crate::ipc_method::{IpcConnection, IpcListener};
23use crate::remote::{
24    RemoteAddressable, RemoteFactoryRegistry, RemoteFactoryShim, RemoteMailboxRegistry,
25    RemoteSpawnable,
26};
27use crate::session::{self, Session};
28
29pub mod command;
30
31mod event;
32pub use event::NodeEvent;
33
34mod context;
35use context::NodeContext;
36
37pub(crate) mod actor_mgr;
38use actor_mgr::{ActorLabelMap, ActorMgr};
39
40type Result<T> = std::result::Result<T, NodeError>;
41
42/// An actor which helps to manage the IPC connections.
43///
44/// The node can hold multiple [`IpcListener`]s to accept incoming IPC connections on several
45/// endpoints in parallel. Outbound connections are initiated by sending a
46/// [`Connect<C>`][command::Connect] command.
47pub struct Node {
48    listener_labels: HashSet<String>,
49    listeners: Vec<Box<dyn IpcListener>>,
50    /// Registers remote addressable actors, the key is the actor's index (local part only, not
51    /// the stable type id), the value is the actor's `RemoteMailbox`.
52    registry: RemoteMailboxRegistry,
53    /// Actor manager, which handles the creation of remote spawnable actors.
54    actor_mgr: Option<Address<ActorMgr>>,
55    //
56    sessions: DoubleMap<ActorId, String, Address<Session>>,
57    children: HashMap<Recipient<Signal>, JoinHandle<()>>,
58    observers: ObserverSet<NodeEvent>,
59    /// Registers remote spawnable actor types, the key is the actor's stable type id (as a
60    /// `u64`), the value is a `RemoteFactory` trait object.
61    ///
62    /// The `ActorMgr` actor will take the ownership of this registry in `post_start` and leave a
63    /// `None` here.
64    _factories: Option<RemoteFactoryRegistry>,
65    /// Maps actor labels to actor ids for remote addressable actors, so they can be looked up by
66    /// a user-friendly label.
67    ///
68    /// The `ActorMgr` actor will take the ownership of this map in `post_start` and leave a `None`
69    /// here.
70    _actor_labels: Option<ActorLabelMap>,
71}
72
73impl Default for Node {
74    #[inline]
75    fn default() -> Self {
76        Self {
77            listener_labels: HashSet::default(),
78            listeners: Vec::new(),
79            registry: RemoteMailboxRegistry::default(),
80            actor_mgr: None,
81            sessions: DoubleMap::default(),
82            children: HashMap::default(),
83            observers: ObserverSet::new(),
84            _factories: Some(RemoteFactoryRegistry::default()),
85            _actor_labels: Some(ActorLabelMap::default()),
86        }
87    }
88}
89
90impl Node {
91    /// Constructs a new [`Node`].
92    pub fn new() -> Self {
93        Self::default()
94    }
95
96    /// Adds an IPC listener to the node.
97    ///
98    /// If the node already has a listener listening on the same endpoint, the new listener will
99    /// replace the existing one, since the node is not started yet when this method is available.
100    /// Note this is not the same as the [`AddListener`][command::AddListener] command, which will
101    /// not replace the existing listener.
102    pub fn with_listener<L>(mut self, listener: L) -> Self
103    where
104        L: IpcListener,
105    {
106        if self.listener_labels.contains(listener.local_endpoint()) {
107            self.listeners
108                .retain(|l| l.local_endpoint() != listener.local_endpoint());
109        } else {
110            self.listener_labels
111                .insert(listener.local_endpoint().to_string());
112        }
113        self.listeners.push(Box::new(listener));
114        self
115    }
116
117    /// Adds an remote addressable actor to the node.
118    ///
119    /// It also registers a label for the actor so that remote actors can look it up by a more
120    /// user-friendly name.
121    ///
122    /// Duplicate actors and labels are silently skipped.
123    pub fn with_actor<A, S>(mut self, label: S, actor: Address<A>) -> Self
124    where
125        A: Actor + RemoteAddressable,
126        S: AsRef<str>,
127    {
128        if let Some(actor_labels) = &mut self._actor_labels {
129            let actor_id = actor.index();
130            if !actor_labels.contains_key(label.as_ref()) && self.registry.insert(actor.into()) {
131                actor_labels.insert(label.as_ref().to_string(), actor_id.as_local());
132            }
133        }
134        self
135    }
136
137    /// Adds an remote spawnable actor factory to the node.
138    ///
139    /// Remote nodes can create instances of this actor type by sending a `CreateActor` node
140    /// command.
141    pub fn with_factory<A>(mut self) -> Self
142    where
143        A: Actor + RemoteSpawnable,
144    {
145        if let Some(factories) = &mut self._factories {
146            factories.insert(
147                A::TYPE_ID.as_u64(),
148                Arc::new(RemoteFactoryShim::<A>(PhantomData)),
149            );
150        }
151        self
152    }
153
154    async fn create_session(
155        &mut self,
156        connection: Box<dyn IpcConnection>,
157        session_label: Option<String>,
158        ctx: &mut <Self as Actor>::Context,
159    ) -> Result<Address<Session>> {
160        let endpoint = connection.peer_endpoint().to_string();
161
162        let label = session_label.unwrap_or_else(|| endpoint.clone());
163
164        if self.sessions.contains_key2(&label) {
165            return Err(NodeError::CreateSessionFailed(
166                format!("session with label '{}' already exists", label).into(),
167            ));
168        }
169
170        let session = Session::new(
171            connection,
172            self.registry.clone(),
173            self.actor_mgr.clone().ok_or_else(|| {
174                NodeError::CreateSessionFailed("actor manager does not exist".into())
175            })?,
176        );
177
178        let (address, join_handle) = Session::create(endpoint.clone(), |child_ctx| {
179            child_ctx.set_supervisor(Some(ctx.address().into()));
180            Ok(session)
181        })
182        .map_err(|e| NodeError::CreateSessionFailed(e.into()))?;
183
184        let session_id = address.index();
185
186        // this will never fail since we have verified the session label is unique and the session id
187        // is also unique as an actor id
188        let _ = self
189            .sessions
190            .insert(session_id, label.clone(), address.clone());
191
192        self.children.insert(address.clone().into(), join_handle);
193
194        self.notify_observers(NodeEvent::SessionCreated(address.clone(), label))
195            .await;
196
197        Ok(address)
198    }
199
200    fn get_session(&self, session_ref: &ActorRef) -> Result<Address<Session>> {
201        match session_ref {
202            ActorRef::Index(index) => self
203                .sessions
204                .get_by_key1(index)
205                .cloned()
206                .ok_or_else(|| NodeError::SessionNotFound(index.to_string())),
207
208            ActorRef::Label(label) => self
209                .sessions
210                .get_by_key2(label)
211                .cloned()
212                .ok_or_else(|| NodeError::SessionNotFound(label.clone())),
213        }
214    }
215}
216
217impl Actor for Node {
218    type Context = NodeContext;
219    type Error = NodeError;
220
221    async fn post_start(&mut self, _ctx: &mut Self::Context) -> Result<()> {
222        let factories = self._factories.take().unwrap_or_default();
223        let actor_labels = self._actor_labels.take().unwrap_or_default();
224
225        // the ActorMgr never fail, so it is not supervised
226        let (address, join_handle) =
227            ActorMgr::new(self.registry.clone(), actor_labels, factories).start("mgr")?;
228        self.children.insert(address.clone().into(), join_handle);
229        self.actor_mgr = Some(address);
230
231        info!("Node is ready");
232
233        Ok(())
234    }
235
236    async fn post_stop(&mut self, _ctx: &mut Self::Context) -> Result<()> {
237        join_all(
238            self.children
239                .drain()
240                .map(|(address, join_handle)| terminate_actor(address, join_handle)),
241        )
242        .await;
243
244        info!("Node is stopped");
245
246        Ok(())
247    }
248}
249
250impl SubjectActor<NodeEvent> for Node {
251    fn observers_mut(&mut self) -> &mut ObserverSet<NodeEvent> {
252        &mut self.observers
253    }
254}
255
256impl<L> Handler<command::AddListener<L>> for Node
257where
258    L: IpcListener,
259{
260    type Result = bool;
261
262    async fn handle(
263        &mut self,
264        msg: command::AddListener<L>,
265        _ctx: &mut Self::Context,
266    ) -> Self::Result {
267        debug_trace!("Handle command {:?}", msg,);
268
269        let label = msg.0.local_endpoint();
270        if self.listener_labels.contains(label) {
271            false
272        } else {
273            self.listener_labels.insert(label.to_string());
274            self.listeners.push(Box::new(msg.0));
275            true
276        }
277    }
278}
279
280impl Handler<command::RemoveListener> for Node {
281    type Result = bool;
282
283    async fn handle(
284        &mut self,
285        msg: command::RemoveListener,
286        ctx: &mut Self::Context,
287    ) -> Self::Result {
288        debug_trace!("Handle command {:?}", msg);
289
290        let label = msg.0;
291        if self.listener_labels.remove(&label) {
292            ctx.abort_accept_task(&label);
293            true
294        } else {
295            false
296        }
297    }
298}
299
300impl<T> Handler<command::Connect<T>> for Node
301where
302    T: IpcConnection,
303{
304    type Result = Result<Address<Session>>;
305
306    async fn handle(&mut self, msg: command::Connect<T>, ctx: &mut Self::Context) -> Self::Result {
307        debug_trace!("Handle command {:?}", msg);
308
309        let command::Connect {
310            endpoint,
311            session_label,
312            ..
313        } = msg;
314
315        let connection = T::connect(&endpoint).await?;
316        let connection: Box<dyn IpcConnection> = Box::new(connection);
317        let address = self.create_session(connection, session_label, ctx).await?;
318
319        Ok(address)
320    }
321}
322
323impl<A> Handler<command::AddActor<A>> for Node
324where
325    A: Actor + RemoteAddressable,
326{
327    type Result = bool;
328
329    async fn handle(
330        &mut self,
331        msg: command::AddActor<A>,
332        _ctx: &mut Self::Context,
333    ) -> Self::Result {
334        debug_trace!("Handle command {:?}", msg);
335
336        if let Some(actor_mgr) = &self.actor_mgr
337            && let Ok(rx) = actor_mgr.send(msg).await
338        {
339            return rx.await.unwrap_or(false);
340        }
341
342        false
343    }
344}
345
346impl Handler<command::RemoveActor> for Node {
347    type Result = bool;
348
349    async fn handle(
350        &mut self,
351        msg: command::RemoveActor,
352        _ctx: &mut Self::Context,
353    ) -> Self::Result {
354        debug_trace!("Handle command {:?}", msg);
355
356        if let Some(actor_mgr) = &self.actor_mgr
357            && let Ok(rx) = actor_mgr.send(msg).await
358        {
359            return rx.await.unwrap_or(false);
360        }
361
362        false
363    }
364}
365
366impl<A> Handler<command::RemoteCreateActor<A>> for Node
367where
368    A: Actor + RemoteSpawnable,
369{
370    type Result = FutureMessageResult<command::RemoteCreateActor<A>>;
371
372    async fn handle(
373        &mut self,
374        msg: command::RemoteCreateActor<A>,
375        _ctx: &mut Self::Context,
376    ) -> Self::Result {
377        debug_trace!("Handle command RemoteCreateActor<{}>", ShortName::of::<A>());
378
379        let command::RemoteCreateActor {
380            session,
381            label,
382            config,
383            ..
384        } = msg;
385
386        let session = self.get_session(&session);
387
388        FutureMessageResult::new(async move {
389            session?
390                .send(session::command::RemoteCreateActor::new(label, config))
391                .await?
392                // this await is time consuming since it involves IPC
393                .await?
394                .map_err(Into::into)
395        })
396    }
397}
398
399impl<A> Handler<command::RemoteGetActor<A>> for Node
400where
401    A: Actor + RemoteAddressable,
402{
403    type Result = FutureMessageResult<command::RemoteGetActor<A>>;
404
405    async fn handle(
406        &mut self,
407        msg: command::RemoteGetActor<A>,
408        _ctx: &mut Self::Context,
409    ) -> Self::Result {
410        debug_trace!("Handle command GetRemoteActor");
411
412        let command::RemoteGetActor { session, actor, .. } = msg;
413
414        let session = self.get_session(&session);
415
416        FutureMessageResult::new(async move {
417            session?
418                .send(session::command::RemoteGetActor::new(actor))
419                .await?
420                // this await is time consuming since it involves IPC
421                .await?
422                .map_err(Into::into)
423        })
424    }
425}
426
427impl Handler<SupervisionEvent<Session>> for Node {
428    type Result = ();
429
430    async fn handle(
431        &mut self,
432        msg: SupervisionEvent<Session>,
433        _ctx: &mut Self::Context,
434    ) -> Self::Result {
435        debug_trace!("Handle supervision event {:?}", msg);
436
437        match msg {
438            SupervisionEvent::Warn(actor, e) => {
439                warn!("Session {} error: {}", actor.index(), e.report());
440            }
441            SupervisionEvent::Terminated(session, e) => {
442                if let Some(e) = e {
443                    error!(
444                        "Session {} is stopped with error: {}",
445                        session.index(),
446                        e.report()
447                    );
448                }
449
450                self.sessions.retain(|_, _, v| v != &session);
451                self.children.remove(&session.clone().into());
452
453                self.notify_observers(NodeEvent::SessionDeleted(session))
454                    .await;
455            }
456            _ => {}
457        }
458    }
459}