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 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}