Skip to main content

reifydb_runtime/
lib.rs

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