monolake_core/config/
mod.rs

1//! Runtime configuration and service setup for asynchronous networking applications.
2//!
3//! This module provides structures and enums for configuring the runtime environment
4//! and services in networking applications. It includes options for worker threads,
5//! I/O event handling, and runtime type selection.
6//!
7//! # Key Components
8//!
9//! - [`ServiceConfig`]: A generic configuration structure for services.
10//! - [`RuntimeConfig`]: Configuration options for the runtime environment.
11//! - [`RuntimeType`]: Enum representing different runtime implementation options.
12use std::num::NonZeroUsize;
13
14use serde::{Deserialize, Serialize};
15
16// Default iouring/epoll entries: 32k
17const DEFAULT_ENTRIES: u32 = 32768;
18
19pub const FALLBACK_PARALLELISM: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(1) };
20
21/// Configuration structure for a service, combining listener and server configs.
22///
23/// # Type Parameters
24///
25/// - `LC`: The type of the listener configuration.
26/// - `SC`: The type of the server configuration.
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ServiceConfig<LC, SC> {
29    /// Configuration for the service listener.
30    pub listener: LC,
31    /// Configuration for the server component of the service.
32    #[serde(flatten)]
33    pub server: SC,
34}
35
36/// Configuration options for the runtime environment.
37///
38/// This structure allows for fine-tuning of the runtime, including worker threads,
39/// I/O multiplexing, and CPU affinity settings.
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct RuntimeConfig {
42    /// Number of worker threads for the runtime.
43    #[serde(default = "default_workers")]
44    pub worker_threads: usize,
45
46    /// Number of I/O entries for event handling for io_uring.
47    #[serde(default = "default_entries")]
48    pub entries: u32,
49
50    /// Idle timeout for squall polling (io_uring specific).
51    pub sqpoll_idle: Option<u32>,
52
53    /// The type of runtime to use.
54    #[serde(default)]
55    pub runtime_type: RuntimeType,
56
57    /// Whether to enable CPU affinity for worker threads.
58    #[serde(default = "default_cpu_affinity")]
59    pub cpu_affinity: bool,
60
61    /// Optional thread pool size for specific runtime implementations.
62    pub thread_pool: Option<usize>,
63}
64
65impl Default for RuntimeConfig {
66    fn default() -> Self {
67        RuntimeConfig {
68            worker_threads: default_workers(),
69            entries: default_entries(),
70            sqpoll_idle: None,
71            runtime_type: Default::default(),
72            cpu_affinity: default_cpu_affinity(),
73            thread_pool: None,
74        }
75    }
76}
77
78/// Enum representing different runtime implementation options.
79///
80/// This allows for selection between different runtime backends,
81/// such as io_uring on Linux or a legacy implementation on other platforms.
82#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
83#[serde(rename_all = "snake_case")]
84pub enum RuntimeType {
85    /// io_uring-based runtime (Linux only).
86    #[cfg(target_os = "linux")]
87    IoUring,
88
89    /// Legacy runtime implementation.
90    Legacy,
91}
92impl Default for RuntimeType {
93    #[cfg(target_os = "linux")]
94    fn default() -> Self {
95        Self::IoUring
96    }
97    #[cfg(not(target_os = "linux"))]
98    fn default() -> Self {
99        Self::Legacy
100    }
101}
102
103macro_rules! define_const {
104    ($name: ident, $val: expr, $type: ty) => {
105        const fn $name() -> $type {
106            $val
107        }
108    };
109}
110
111fn default_workers() -> usize {
112    std::thread::available_parallelism()
113        .unwrap_or(FALLBACK_PARALLELISM)
114        .into()
115}
116
117define_const!(default_entries, DEFAULT_ENTRIES, u32);
118define_const!(default_cpu_affinity, false, bool);
119
120// #[cfg(test)]
121// mod tests {
122//     use super::Config;
123
124//     #[test]
125//     fn test_json_deserialize() {
126//         const TEST_CONFIG: &str =
127//             "
128//             {
129//                 \"servers\": {
130//                     \"test-server\": {
131//                         \
132//              \"name\": \"test\",
133//                                    \"listener\": {\"socket_addr\" : \
134//              \"0.0.0.0:8080\"},
135//                                    \"routes\": [{
136//                             \
137//              \"path\": \"/\",
138//                             \"upstreams\": [{
139//                                 \
140//              \"endpoint\": {\"uds_path\":\"/tmp/test\"},\"weight\": 1 }, {
141//                                 \
142//              \"endpoint\": {\"uri\":\"https://gateway.example.com/\"},\"weight\": 2 }] }]
143//                     }
144//                 }
145//             }
146//         ";
147
148//         let config = Config::from_slice(TEST_CONFIG.as_bytes()).unwrap();
149//         assert_eq!("test-server", config.servers.keys().next().unwrap());
150//     }
151
152//     #[test]
153//     fn test_toml_deserialize() {
154//         const TEST_CONFIG: &str = "
155//             [servers.test-server]
156//             name = 'gateway.example.com'
157//             listener = { socket_addr = '[::]:8080' }
158
159//             [[servers.test-server.routes]]
160//             path = '/'
161//             id = 'test'
162
163//             [[servers.test-server.routes.upstreams]]
164//             endpoint = {uri = 'test'}
165//             weight = 1
166
167//             [[servers.test-server.routes.upstreams]]
168//             endpoint = {uds_path = '/tmp/def.sock'}
169//             weight = 2
170//         ";
171
172//         let config: Config = Config::from_slice(TEST_CONFIG.as_bytes()).unwrap();
173//         assert_eq!("test-server", config.servers.keys().next().unwrap());
174//     }
175// }