hojicha_runtime/
shared_runtime.rs

1//! Shared Tokio runtime for the entire application
2//!
3//! This module provides a single shared runtime instance to avoid
4//! the anti-pattern of creating multiple runtimes.
5
6use std::sync::{Arc, OnceLock};
7use tokio::runtime::{Builder, Runtime};
8
9static SHARED_RUNTIME: OnceLock<Arc<Runtime>> = OnceLock::new();
10
11/// Get or create the shared runtime instance
12pub fn shared_runtime() -> Arc<Runtime> {
13    SHARED_RUNTIME
14        .get_or_init(|| {
15            Arc::new(
16                Builder::new_multi_thread()
17                    .worker_threads(get_cpu_count())
18                    .thread_name("hojicha-async")
19                    .enable_all()
20                    .build()
21                    .expect("Failed to create Tokio runtime"),
22            )
23        })
24        .clone()
25}
26
27/// Configuration for the shared runtime
28#[derive(Debug, Clone)]
29pub struct RuntimeConfig {
30    /// Number of worker threads (default: number of CPU cores)
31    pub worker_threads: Option<usize>,
32    /// Thread name prefix (default: "hojicha-async")
33    pub thread_name_prefix: String,
34    /// Enable I/O driver (default: true)
35    pub enable_io: bool,
36    /// Enable time driver (default: true)
37    pub enable_time: bool,
38}
39
40impl Default for RuntimeConfig {
41    fn default() -> Self {
42        Self {
43            worker_threads: None,
44            thread_name_prefix: "hojicha-async".to_string(),
45            enable_io: true,
46            enable_time: true,
47        }
48    }
49}
50
51/// Initialize the shared runtime with custom configuration
52pub fn initialize_runtime(config: RuntimeConfig) -> Result<(), std::io::Error> {
53    if SHARED_RUNTIME.get().is_some() {
54        return Ok(()); // Already initialized
55    }
56
57    let mut builder = Builder::new_multi_thread();
58
59    if let Some(threads) = config.worker_threads {
60        builder.worker_threads(threads);
61    } else {
62        builder.worker_threads(get_cpu_count());
63    }
64
65    builder.thread_name(&config.thread_name_prefix);
66
67    if config.enable_io && config.enable_time {
68        builder.enable_all();
69    } else {
70        if config.enable_io {
71            builder.enable_io();
72        }
73        if config.enable_time {
74            builder.enable_time();
75        }
76    }
77
78    let runtime = Arc::new(builder.build()?);
79    let _ = SHARED_RUNTIME.set(runtime);
80    Ok(())
81}
82
83/// Get the number of available CPU cores
84fn get_cpu_count() -> usize {
85    std::thread::available_parallelism()
86        .map(|n| n.get())
87        .unwrap_or(4)
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_shared_runtime_singleton() {
96        let rt1 = shared_runtime();
97        let rt2 = shared_runtime();
98        assert!(Arc::ptr_eq(&rt1, &rt2));
99    }
100}