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