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