Skip to main content

reifydb_runtime/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4#![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}