1#![cfg_attr(not(debug_assertions), deny(clippy::disallowed_methods))]
5#![cfg_attr(debug_assertions, warn(clippy::disallowed_methods))]
6#![allow(clippy::tabs_in_doc_comments)]
7
8pub mod cache;
9
10pub mod context;
11
12pub mod fatal;
13
14pub mod io;
15
16pub mod pool;
17
18pub mod shutdown;
19
20pub mod sync;
21
22pub mod actor;
23
24pub mod version_epoch;
25
26#[cfg(reifydb_dst)]
27pub mod testing;
28
29#[cfg(not(reifydb_dst))]
30use std::future::Future;
31use std::thread;
32
33use reifydb_value::Result as ValueResult;
34
35use crate::{
36 actor::system::ActorSystem,
37 context::clock::{Clock, MockClock},
38 pool::{PoolConfig, Pools},
39 shutdown::{Shutdown, ShutdownErrors},
40};
41
42#[derive(Clone)]
43pub struct RuntimeConfig {
44 pub clock: Clock,
45 pub rng: context::rng::Rng,
46 pub fatal: fatal::FatalConfig,
47}
48
49impl Default for RuntimeConfig {
50 fn default() -> Self {
51 Self {
52 clock: Clock::Real,
53 rng: context::rng::Rng::default(),
54 fatal: fatal::FatalConfig::default(),
55 }
56 }
57}
58
59impl RuntimeConfig {
60 pub fn seeded(mut self, seed: u64) -> Self {
61 self.clock = Clock::Mock(MockClock::from_millis(seed));
62 self.rng = context::rng::Rng::seeded(seed);
63 self
64 }
65
66 pub fn fatal(mut self, config: fatal::FatalConfig) -> Self {
67 self.fatal = config;
68 self
69 }
70
71 pub fn clock(mut self, clock: Clock) -> Self {
72 self.clock = clock;
73 self
74 }
75}
76
77use std::fmt;
78#[cfg(target_arch = "wasm32")]
79use std::{
80 pin::Pin,
81 task::{Context, Poll},
82};
83
84#[cfg(target_arch = "wasm32")]
85use futures_util::future::LocalBoxFuture;
86#[cfg(all(reifydb_target = "host", not(reifydb_dst)))]
87use tokio::runtime as tokio_runtime;
88#[cfg(all(reifydb_target = "host", not(reifydb_dst)))]
89use tokio::task::JoinHandle;
90
91#[cfg(target_arch = "wasm32")]
92#[derive(Clone, Copy, Debug)]
93pub struct WasmHandle;
94
95#[cfg(target_arch = "wasm32")]
96pub struct WasmJoinHandle<T> {
97 future: LocalBoxFuture<'static, T>,
98}
99
100#[cfg(target_arch = "wasm32")]
101impl<T> Future for WasmJoinHandle<T> {
102 type Output = Result<T, WasmJoinError>;
103
104 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
105 match self.future.as_mut().poll(cx) {
106 Poll::Ready(v) => Poll::Ready(Ok(v)),
107 Poll::Pending => Poll::Pending,
108 }
109 }
110}
111
112#[cfg(target_arch = "wasm32")]
113#[derive(Debug)]
114pub struct WasmJoinError;
115
116#[cfg(target_arch = "wasm32")]
117impl fmt::Display for WasmJoinError {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 write!(f, "WASM task failed")
120 }
121}
122
123#[cfg(target_arch = "wasm32")]
124use std::error::Error;
125
126#[cfg(target_arch = "wasm32")]
127impl Error for WasmJoinError {}
128
129use crate::actor::system::ActorSpawner;
130
131pub struct Runtime {
132 system: ActorSystem,
133 pools: Pools,
134 clock: Clock,
135 rng: context::rng::Rng,
136}
137
138impl Runtime {
139 pub fn from_config(config: RuntimeConfig, pools: PoolConfig) -> Self {
140 let pools = Pools::new(pools);
141 let system = ActorSystem::new(pools.clone(), config.clock.clone());
142
143 Self {
144 system,
145 pools,
146 clock: config.clock,
147 rng: config.rng,
148 }
149 }
150
151 pub fn handle(&self) -> RuntimeHandle {
152 RuntimeHandle {
153 system: self.system.clone(),
154 pools: self.pools.clone(),
155 clock: self.clock.clone(),
156 rng: self.rng.clone(),
157 }
158 }
159
160 pub fn actor_system(&self) -> ActorSystem {
161 self.system.clone()
162 }
163
164 pub fn spawner(&self) -> ActorSpawner {
165 self.system.spawner()
166 }
167
168 pub fn clock(&self) -> &Clock {
169 &self.clock
170 }
171
172 pub fn rng(&self) -> &context::rng::Rng {
173 &self.rng
174 }
175
176 #[cfg(all(reifydb_target = "host", not(reifydb_dst)))]
177 pub fn tokio(&self) -> tokio_runtime::Handle {
178 self.pools.handle()
179 }
180
181 #[cfg(target_arch = "wasm32")]
182 pub fn tokio(&self) -> WasmHandle {
183 WasmHandle
184 }
185
186 #[cfg(all(reifydb_target = "host", not(reifydb_dst)))]
187 pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
188 where
189 F: Future + Send + 'static,
190 F::Output: Send + 'static,
191 {
192 self.pools.spawn(future)
193 }
194
195 #[cfg(target_arch = "wasm32")]
196 pub fn spawn<F>(&self, future: F) -> WasmJoinHandle<F::Output>
197 where
198 F: Future + 'static,
199 F::Output: 'static,
200 {
201 WasmJoinHandle {
202 future: Box::pin(future),
203 }
204 }
205
206 #[cfg(all(reifydb_target = "host", not(reifydb_dst)))]
207 pub fn block_on<F>(&self, future: F) -> F::Output
208 where
209 F: Future,
210 {
211 self.pools.block_on(future)
212 }
213
214 #[cfg(target_arch = "wasm32")]
215 pub fn block_on<F>(&self, _future: F) -> F::Output
216 where
217 F: Future,
218 {
219 unimplemented!("block_on not supported in WASM - use async execution instead")
220 }
221}
222
223impl Shutdown for Runtime {
224 fn shutdown(&self) -> ValueResult<()> {
225 let mut errors = ShutdownErrors::new("runtime");
226 self.system.shutdown();
227 if let Err(err) = self.system.join() {
228 errors.fail("actor system", err);
229 }
230 self.pools.shutdown();
231 errors.finish()
232 }
233}
234
235impl Drop for Runtime {
236 fn drop(&mut self) {
237 if let Err(err) = self.shutdown() {
238 if thread::panicking() {
239 eprintln!("runtime shutdown failed during unwind: {err}");
240 } else {
241 panic!("runtime shutdown failed: {err}");
242 }
243 }
244 }
245}
246
247impl fmt::Debug for Runtime {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 f.debug_struct("Runtime").finish_non_exhaustive()
250 }
251}
252
253#[derive(Clone)]
254pub struct RuntimeHandle {
255 system: ActorSystem,
256 pools: Pools,
257 clock: Clock,
258 rng: context::rng::Rng,
259}
260
261impl RuntimeHandle {
262 pub fn actor_system(&self) -> ActorSystem {
263 self.system.clone()
264 }
265
266 pub fn spawner(&self) -> ActorSpawner {
267 self.system.spawner()
268 }
269
270 pub fn pools(&self) -> Pools {
271 self.pools.clone()
272 }
273
274 pub fn clock(&self) -> &Clock {
275 &self.clock
276 }
277
278 pub fn rng(&self) -> &context::rng::Rng {
279 &self.rng
280 }
281
282 #[cfg(all(reifydb_target = "host", not(reifydb_dst)))]
283 pub fn tokio(&self) -> tokio_runtime::Handle {
284 self.pools.handle()
285 }
286
287 #[cfg(target_arch = "wasm32")]
288 pub fn tokio(&self) -> WasmHandle {
289 WasmHandle
290 }
291
292 #[cfg(all(reifydb_target = "host", not(reifydb_dst)))]
293 pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
294 where
295 F: Future + Send + 'static,
296 F::Output: Send + 'static,
297 {
298 self.pools.spawn(future)
299 }
300
301 #[cfg(target_arch = "wasm32")]
302 pub fn spawn<F>(&self, future: F) -> WasmJoinHandle<F::Output>
303 where
304 F: Future + 'static,
305 F::Output: 'static,
306 {
307 WasmJoinHandle {
308 future: Box::pin(future),
309 }
310 }
311
312 #[cfg(all(reifydb_target = "host", not(reifydb_dst)))]
313 pub fn block_on<F>(&self, future: F) -> F::Output
314 where
315 F: Future,
316 {
317 self.pools.block_on(future)
318 }
319
320 #[cfg(target_arch = "wasm32")]
321 pub fn block_on<F>(&self, _future: F) -> F::Output
322 where
323 F: Future,
324 {
325 unimplemented!("block_on not supported in WASM - use async execution instead")
326 }
327}
328
329impl fmt::Debug for RuntimeHandle {
330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 f.debug_struct("RuntimeHandle").finish_non_exhaustive()
332 }
333}
334
335#[cfg(all(test, not(reifydb_single_threaded)))]
336mod tests {
337 use super::*;
338
339 fn test_config() -> RuntimeConfig {
340 RuntimeConfig::default()
341 }
342
343 fn test_pools() -> PoolConfig {
344 PoolConfig {
345 coordination_threads: 2,
346 flow_threads: 2,
347 maintenance_threads: 1,
348 task_threads: 2,
349 compute_threads: 2,
350 async_threads: 2,
351 }
352 }
353
354 #[test]
355 fn test_runtime_creation() {
356 let runtime = Runtime::from_config(test_config(), test_pools());
357 let result = runtime.block_on(async { 42 });
358 assert_eq!(result, 42);
359 }
360
361 #[test]
362 fn test_spawn() {
363 let runtime = Runtime::from_config(test_config(), test_pools());
364 let handle = runtime.spawn(async { 123 });
365 let result = runtime.block_on(handle).unwrap();
366 assert_eq!(result, 123);
367 }
368
369 #[test]
370 fn test_actor_system_accessible() {
371 let runtime = Runtime::from_config(test_config(), test_pools());
372 let _system = runtime.actor_system();
373 }
374
375 #[test]
376 fn test_shutdown_drops_runtime() {
377 let runtime = Runtime::from_config(test_config(), test_pools());
378 let spawner = runtime.spawner();
379 assert!(spawner.is_alive());
380 drop(runtime);
381 assert!(!spawner.is_alive());
382 }
383}