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