Skip to main content

reifydb_runtime/actor/mailbox/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4#[cfg(all(reifydb_single_threaded, not(reifydb_dst)))]
5use std::cell::{Cell, RefCell};
6use std::fmt;
7#[cfg(all(reifydb_single_threaded, not(reifydb_dst)))]
8use std::rc::Rc;
9#[cfg(all(reifydb_single_threaded, not(reifydb_dst)))]
10use std::sync::Arc;
11#[cfg(all(reifydb_single_threaded, not(reifydb_dst)))]
12use std::sync::atomic::AtomicBool;
13#[cfg(reifydb_target = "wasm")]
14use std::time::Duration;
15
16use cfg_if::cfg_if;
17
18#[cfg(reifydb_target = "wasm")]
19use crate::actor::timers::wasm::schedule_once_fn;
20
21#[cfg(not(reifydb_single_threaded))]
22pub(crate) mod host;
23
24#[cfg(all(reifydb_single_threaded, not(reifydb_dst)))]
25pub(crate) mod wasm;
26
27#[cfg(reifydb_dst)]
28pub(crate) mod dst;
29
30cfg_if! {
31	if #[cfg(reifydb_dst)] {
32		type ActorRefInnerImpl<M> = dst::ActorRefInner<M>;
33	} else if #[cfg(not(reifydb_single_threaded))] {
34		type ActorRefInnerImpl<M> = host::ActorRefInner<M>;
35	} else {
36		type ActorRefInnerImpl<M> = wasm::ActorRefInner<M>;
37	}
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum SendError<M> {
42	Closed(M),
43
44	Full(M),
45}
46
47impl<M> SendError<M> {
48	#[inline]
49	pub fn into_inner(self) -> M {
50		match self {
51			SendError::Closed(m) => m,
52			SendError::Full(m) => m,
53		}
54	}
55}
56
57impl<M: fmt::Debug> fmt::Display for SendError<M> {
58	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59		match self {
60			SendError::Closed(_) => write!(f, "actor mailbox closed"),
61			SendError::Full(_) => write!(f, "actor mailbox full"),
62		}
63	}
64}
65
66impl<M: fmt::Debug> error::Error for SendError<M> {}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub enum AskError {
70	SendFailed,
71
72	ResponseClosed,
73}
74
75impl fmt::Display for AskError {
76	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77		match self {
78			AskError::SendFailed => write!(f, "failed to send ask request"),
79			AskError::ResponseClosed => write!(f, "response channel closed"),
80		}
81	}
82}
83
84impl error::Error for AskError {}
85
86pub struct ActorRef<M> {
87	inner: ActorRefInnerImpl<M>,
88}
89
90impl<M> Clone for ActorRef<M> {
91	#[inline]
92	fn clone(&self) -> Self {
93		Self {
94			inner: self.inner.clone(),
95		}
96	}
97}
98
99impl<M> fmt::Debug for ActorRef<M> {
100	#[inline]
101	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102		self.inner.fmt(f)
103	}
104}
105
106// SAFETY: under reifydb_single_threaded there is no second thread, so an ActorRef can never be reached
107// from another one. These impls only satisfy Send + Sync bounds and never back a real transfer.
108#[cfg(reifydb_single_threaded)]
109unsafe impl<M> Send for ActorRef<M> {}
110
111#[cfg(reifydb_single_threaded)]
112unsafe impl<M> Sync for ActorRef<M> {}
113
114impl<M> ActorRef<M> {
115	#[inline]
116	pub(crate) fn from_inner(inner: ActorRefInnerImpl<M>) -> Self {
117		Self {
118			inner,
119		}
120	}
121}
122
123#[cfg(not(reifydb_single_threaded))]
124impl<M: Send> ActorRef<M> {
125	#[inline]
126	pub(crate) fn new(tx: Sender<M>) -> Self {
127		Self {
128			inner: host::ActorRefInner::new(tx),
129		}
130	}
131
132	#[inline]
133	pub(crate) fn set_notify(&self, f: sync::Arc<dyn Fn() + Send + Sync>) {
134		self.inner.set_notify(f)
135	}
136
137	#[inline]
138	pub fn send(&self, msg: M) -> Result<(), SendError<M>> {
139		self.inner.send(msg)
140	}
141
142	#[inline]
143	pub fn send_deferred(&self, msg: M) -> Result<(), SendError<M>> {
144		self.inner.send(msg)
145	}
146
147	#[inline]
148	pub fn send_blocking(&self, msg: M) -> Result<(), SendError<M>> {
149		self.inner.send_blocking(msg)
150	}
151
152	#[inline]
153	pub fn is_alive(&self) -> bool {
154		self.inner.is_alive()
155	}
156}
157
158#[cfg(reifydb_dst)]
159impl<M> ActorRef<M> {
160	#[inline]
161	pub fn send(&self, msg: M) -> Result<(), SendError<M>> {
162		self.inner.send(msg)
163	}
164
165	#[inline]
166	pub fn send_deferred(&self, msg: M) -> Result<(), SendError<M>> {
167		self.inner.send(msg)
168	}
169
170	#[inline]
171	pub fn send_blocking(&self, msg: M) -> Result<(), SendError<M>> {
172		self.inner.send_blocking(msg)
173	}
174
175	#[inline]
176	pub fn is_alive(&self) -> bool {
177		self.inner.is_alive()
178	}
179
180	#[inline]
181	pub(crate) fn mark_stopped(&self) {
182		self.inner.mark_stopped()
183	}
184
185	#[inline]
186	pub(crate) fn set_notify(&self, f: Box<dyn Fn()>) {
187		self.inner.set_notify(f)
188	}
189}
190
191#[cfg(all(reifydb_single_threaded, not(reifydb_dst)))]
192impl<M> ActorRef<M> {
193	#[inline]
194	pub(crate) fn from_wasm_inner(
195		processor: Rc<RefCell<Option<Box<dyn FnMut(M)>>>>,
196		alive: Arc<AtomicBool>,
197		queue: Rc<RefCell<Vec<M>>>,
198		processing: Rc<Cell<bool>>,
199	) -> Self {
200		Self {
201			inner: wasm::ActorRefInner::new(processor, alive, queue, processing),
202		}
203	}
204
205	#[inline]
206	pub fn send(&self, msg: M) -> Result<(), SendError<M>> {
207		self.inner.send(msg)
208	}
209
210	#[cfg(reifydb_target = "wasm")]
211	pub fn send_deferred(&self, msg: M) -> Result<(), SendError<M>>
212	where
213		M: Send + 'static,
214	{
215		if !self.is_alive() {
216			return Err(SendError::Closed(msg));
217		}
218		schedule_once_fn(self.clone(), Duration::ZERO, move || msg);
219		Ok(())
220	}
221
222	#[cfg(reifydb_target = "wasi")]
223	#[inline]
224	pub fn send_deferred(&self, msg: M) -> Result<(), SendError<M>> {
225		self.inner.send(msg)
226	}
227
228	#[inline]
229	pub fn send_blocking(&self, msg: M) -> Result<(), SendError<M>> {
230		self.inner.send_blocking(msg)
231	}
232
233	#[inline]
234	pub fn is_alive(&self) -> bool {
235		self.inner.is_alive()
236	}
237
238	#[inline]
239	pub(crate) fn mark_stopped(&self) {
240		self.inner.mark_stopped()
241	}
242
243	#[inline]
244	pub(crate) fn processor(&self) -> &Rc<RefCell<Option<Box<dyn FnMut(M)>>>> {
245		&self.inner.processor
246	}
247}
248
249use std::error;
250#[cfg(not(reifydb_single_threaded))]
251use std::sync;
252
253#[cfg(not(reifydb_single_threaded))]
254use crossbeam_channel::Sender;
255#[cfg(reifydb_dst)]
256pub(crate) use dst::create_mailbox as create_dst_mailbox;
257#[cfg(not(reifydb_single_threaded))]
258pub(crate) use host::create_mailbox;
259#[cfg(all(reifydb_single_threaded, not(reifydb_dst)))]
260pub(crate) use wasm::create_actor_ref;