//! Process-wide shared Tokio runtime for sync → async bridging. //! //! Many `Tool::run` implementations are synchronous but need to drive async //! work (LLM calls, subagent execution). Creating a fresh //! [`tokio::runtime::Runtime`] on every call is expensive (spawns a thread //! pool + runtime each time) and can fail randomly under thread pressure. //! //! # Flow //! //! [`runtime()`] returns a lazily-initialised process-wide runtime created //! exactly once via [`std::sync::OnceLock`]. Callers use //! `runtime().block_on(...)` exactly like they would with a local runtime — //! the only difference is the runtime is shared, so the cost is paid once per //! process instead of once per tool call. //! //! # Safety //! //! `block_on` panics if called from within a running Tokio runtime. The //! tools that use this helper are synchronous (`Tool::run`), so this is safe //! in practice. Async code should never call `runtime().block_on`. use std::sync::OnceLock; /// Maximum worker threads for the shared runtime. Kept modest — tools are /// mostly I/O-bound and rarely need more concurrency than this. const RUNTIME_WORKER_THREADS: usize = 8; static SHARED_RUNTIME: OnceLock = OnceLock::new(); /// Return the process-wide shared Tokio runtime, initialising it on first use. /// /// The runtime is configured with `worker_threads = 8` and /// `enable_all()` (time + IO drivers) so streams, timers, and network calls /// all work. If initialisation fails (extremely rare — resource exhaustion at /// startup), the process aborts with a clear message rather than returning /// an error on every subsequent call. pub fn runtime() -> &'static tokio::runtime::Runtime { SHARED_RUNTIME.get_or_init(|| { tokio::runtime::Builder::new_multi_thread() .worker_threads(RUNTIME_WORKER_THREADS) .thread_name("zesdex-shared-rt") .enable_all() .build() .expect("failed to create shared tokio runtime") }) } #[cfg(test)] mod tests { use super::runtime; #[test] fn runtime_is_singleton() { assert!(std::ptr::eq(runtime(), runtime())); } #[test] fn runtime_blocks_and_resolves() { let val = runtime().block_on(async { 6 * 7 }); assert_eq!(val, 42); } }