palladium_actor/bridge.rs
1use crate::envelope::Envelope;
2use crate::errors::SendError;
3use crate::message::MessagePayload;
4use crate::path::{ActorPath, AddrHash};
5use crate::spec::ChildSpec;
6use std::future::Future;
7use std::pin::Pin;
8use std::sync::Arc;
9use std::time::Duration;
10
11/// A cheaply cloneable, type-erased send function that delivers directly to a
12/// cached `MailboxSender`, bypassing the global `TransportRegistry`.
13///
14/// Created by [`RuntimeBridge::cached_sender`] and stored in each actor's
15/// `AddrCache` keyed by the destination `AddrHash`.
16pub type CachedSendFn =
17 Arc<dyn Fn(Envelope, MessagePayload) -> Result<(), SendError> + Send + Sync + 'static>;
18
19// ── Reactor ───────────────────────────────────────────────────────────────────
20
21/// Handle to a spawned task.
22pub trait SpawnHandle: Send + Sync + 'static {
23 fn abort(&self);
24}
25
26/// Minimal abstraction over the async runtime, provided to actors.
27pub trait Reactor: Send + Sync + 'static {
28 /// Create a clone of this reactor, boxed.
29 fn clone_box(&self) -> Box<dyn Reactor>;
30
31 /// Spawn a task local to the current thread.
32 fn spawn_local(
33 &self,
34 fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
35 ) -> Box<dyn SpawnHandle>;
36
37 /// Spawn a task that can run on any thread.
38 fn spawn(
39 &self,
40 fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
41 ) -> Box<dyn SpawnHandle>;
42
43 /// Yield execution back to the scheduler.
44 fn yield_now(&self) -> Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>;
45
46 /// Sleep for `duration` under the current time model.
47 fn sleep(
48 &self,
49 duration: Duration,
50 ) -> Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>;
51
52 /// Return the current instant under the current time model.
53 fn now(&self) -> std::time::Instant;
54
55 /// Measures nanoseconds elapsed since `start`.
56 fn elapsed_since(&self, start: std::time::Instant) -> u64;
57
58 /// Create a periodic interval that ticks every `duration`.
59 fn create_interval(&self, duration: Duration) -> Box<dyn Interval>;
60
61 /// Return a random u64 from the reactor's RNG.
62 fn next_u64(&self) -> u64;
63}
64
65/// A periodic timer that can be polled or awaited.
66pub trait Interval: Send + 'static {
67 /// Wait for the next tick of the interval.
68 fn tick(&mut self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
69}
70
71// ── RuntimeBridge ─────────────────────────────────────────────────────────────
72
73/// Bridge between `ActorContext` methods and the runtime event loop.
74///
75/// Implemented by `pd-runtime`; injected into each actor's context at spawn
76/// time. Uses only types defined in `pd-actor` to avoid a circular
77/// crate dependency.
78pub trait RuntimeBridge<R: Reactor>: Reactor {
79 /// Access the underlying reactor implementation.
80 fn reactor(&self) -> &R;
81
82 /// Spawn a child actor under `parent`'s path. Returns the child's
83 /// `AddrHash` (derived synchronously from the path); the runtime processes
84 /// the spawn asynchronously.
85 fn spawn_child(&self, spec: ChildSpec<R>, parent: &ActorPath) -> AddrHash;
86
87 /// Signal the event loop to stop this actor after the current handler
88 /// returns.
89 fn stop_self(&self);
90
91 /// Route a pre-built envelope through the local transport layer.
92 fn route(&self, envelope: Envelope, payload: MessagePayload) -> Result<(), SendError>;
93
94 /// Schedule `payload` to be sent at `envelope.destination` after `delay`.
95 fn send_after(&self, delay: Duration, envelope: Envelope, payload: MessagePayload);
96
97 /// Resolve an actor's current address by its path.
98 fn lookup_path(&self, path: &ActorPath) -> Option<AddrHash>;
99
100 /// Resolve an actor's path from its address hash.
101 ///
102 /// Used by `ActorContext::send_raw` to enforce namespace policy.
103 /// Returns `None` if the address is unknown (e.g., synthetic or external);
104 /// callers fail open in that case.
105 fn resolve_addr(&self, hash: AddrHash) -> Option<ActorPath> {
106 let _ = hash;
107 None
108 }
109
110 /// Return a direct `CachedSendFn` for `addr` if it is a local actor.
111 ///
112 /// The returned closure bypasses the global `TransportRegistry` and sends
113 /// directly to the actor's `MailboxSender`. Returns `None` when the
114 /// destination is not local (remote / federated actors).
115 ///
116 /// Default implementation returns `None`; `RuntimeBridgeImpl` overrides
117 /// this to perform the DashMap lookup and wrap the sender.
118 fn cached_sender(&self, addr: AddrHash) -> Option<CachedSendFn> {
119 let _ = addr;
120 None
121 }
122}