hiver_runtime/driver/
config.rs1use std::sync::Arc;
10
11use crate::driver::Driver;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum DriverType {
17 Epoll,
19 IOUring,
21 Kqueue,
23 Auto,
26}
27
28#[derive(Debug, Clone, Copy)]
31pub struct DriverConfig {
32 pub entries: u32,
35 pub submit_wait: bool,
38 pub cpu_affinity: Option<usize>,
41 pub defer_wakeup: bool,
44 pub max_ops_per_fd: u32,
47}
48
49impl Default for DriverConfig {
50 fn default() -> Self {
51 Self {
52 entries: 256,
53 submit_wait: false,
54 cpu_affinity: None,
55 defer_wakeup: true,
56 max_ops_per_fd: 32,
57 }
58 }
59}
60
61#[derive(Debug, Clone)]
67pub struct DriverConfigBuilder {
68 config: DriverConfig,
69}
70
71impl DriverConfigBuilder {
72 #[must_use]
75 pub fn new() -> Self {
76 Self {
77 config: DriverConfig::default(),
78 }
79 }
80
81 #[must_use]
84 pub fn entries(mut self, entries: u32) -> Self {
85 self.config.entries = entries.next_power_of_two();
86 self
87 }
88
89 #[must_use]
92 pub const fn submit_wait(mut self, wait: bool) -> Self {
93 self.config.submit_wait = wait;
94 self
95 }
96
97 #[must_use]
100 pub const fn cpu_affinity(mut self, core: usize) -> Self {
101 self.config.cpu_affinity = Some(core);
102 self
103 }
104
105 #[must_use]
108 pub const fn no_affinity(mut self) -> Self {
109 self.config.cpu_affinity = None;
110 self
111 }
112
113 #[must_use]
116 pub const fn defer_wakeup(mut self, defer: bool) -> Self {
117 self.config.defer_wakeup = defer;
118 self
119 }
120
121 #[must_use]
124 pub const fn max_ops_per_fd(mut self, max: u32) -> Self {
125 self.config.max_ops_per_fd = max;
126 self
127 }
128
129 #[must_use]
132 pub const fn build(self) -> DriverConfig {
133 self.config
134 }
135}
136
137impl Default for DriverConfigBuilder {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143pub struct DriverFactory;
149
150impl DriverFactory {
151 pub fn create(driver_type: DriverType) -> std::io::Result<Arc<dyn Driver>> {
171 Self::create_with_config(driver_type, DriverConfig::default())
172 }
173
174 pub fn create_with_config(
182 driver_type: DriverType,
183 config: DriverConfig,
184 ) -> std::io::Result<Arc<dyn Driver>> {
185 let ty = if matches!(driver_type, DriverType::Auto) {
186 Self::detect_best_driver()?
187 } else {
188 driver_type
189 };
190
191 match ty {
192 #[cfg(target_os = "linux")]
193 DriverType::Epoll => {
194 Ok(Arc::new(crate::driver::epoll::EpollDriver::with_config(config)?))
195 },
196 #[cfg(target_os = "linux")]
197 DriverType::IOUring => {
198 Ok(Arc::new(crate::driver::iouring::IoUringDriver::with_config(config)?))
199 },
200 #[cfg(any(
201 target_os = "macos",
202 target_os = "freebsd",
203 target_os = "netbsd",
204 target_os = "openbsd",
205 target_os = "dragonfly"
206 ))]
207 DriverType::Kqueue => {
208 Ok(Arc::new(crate::driver::kqueue::KqueueDriver::with_config(config)?))
209 },
210 #[cfg(not(target_os = "linux"))]
211 DriverType::Epoll => Err(std::io::Error::new(
212 std::io::ErrorKind::Unsupported,
213 "epoll driver is only available on Linux",
214 )),
215 #[cfg(not(target_os = "linux"))]
216 DriverType::IOUring => Err(std::io::Error::new(
217 std::io::ErrorKind::Unsupported,
218 "io-uring driver is only available on Linux",
219 )),
220 #[cfg(not(any(
221 target_os = "macos",
222 target_os = "freebsd",
223 target_os = "netbsd",
224 target_os = "openbsd",
225 target_os = "dragonfly"
226 )))]
227 DriverType::Kqueue => Err(std::io::Error::new(
228 std::io::ErrorKind::Unsupported,
229 "kqueue driver is only available on macOS/BSD",
230 )),
231 DriverType::Auto => {
232 Err(std::io::Error::new(
237 std::io::ErrorKind::Unsupported,
238 "Failed to detect a suitable driver for this platform",
239 ))
240 },
241 }
242 }
243
244 fn detect_best_driver() -> std::io::Result<DriverType> {
247 #[cfg(target_os = "linux")]
248 {
249 if Self::has_io_uring_support() {
252 Ok(DriverType::IOUring)
253 } else {
254 Ok(DriverType::Epoll)
255 }
256 }
257
258 #[cfg(any(
259 target_os = "macos",
260 target_os = "freebsd",
261 target_os = "netbsd",
262 target_os = "openbsd",
263 target_os = "dragonfly"
264 ))]
265 {
266 Ok(DriverType::Kqueue)
267 }
268
269 #[cfg(not(any(
270 target_os = "linux",
271 target_os = "macos",
272 target_os = "freebsd",
273 target_os = "netbsd",
274 target_os = "openbsd",
275 target_os = "dragonfly"
276 )))]
277 {
278 Err(std::io::Error::new(
279 std::io::ErrorKind::Unsupported,
280 "No suitable driver found for this platform",
281 ))
282 }
283 }
284
285 #[cfg(target_os = "linux")]
288 fn has_io_uring_support() -> bool {
289 let mut uname = libc::utsname {
294 sysname: [0; 65],
295 nodename: [0; 65],
296 release: [0; 65],
297 version: [0; 65],
298 machine: [0; 65],
299 domainname: [0; 65],
300 };
301
302 unsafe {
303 if libc::uname(&mut uname) != 0 {
304 return false;
305 }
306
307 let release = std::ffi::CStr::from_ptr(uname.release.as_ptr()).to_string_lossy();
310
311 if let Some((major, rest)) = release.split_once('.') {
312 if let Some((minor, _)) = rest.split_once('.') {
313 if let (Ok(maj), Ok(min)) = (major.parse::<u32>(), minor.parse::<u32>()) {
314 return maj > 5 || (maj == 5 && min >= 1);
315 }
316 }
317 }
318 }
319
320 false
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327
328 #[test]
329 fn test_config_builder() {
330 let config = DriverConfigBuilder::new()
331 .entries(512)
332 .submit_wait(true)
333 .cpu_affinity(0)
334 .defer_wakeup(false)
335 .build();
336
337 assert_eq!(config.entries, 512);
340 assert!(config.submit_wait);
341 assert_eq!(config.cpu_affinity, Some(0));
342 assert!(!config.defer_wakeup);
343 }
344
345 #[test]
346 fn test_config_rounding() {
347 let config = DriverConfigBuilder::new().entries(100).build();
348
349 assert_eq!(config.entries, 128);
352 }
353
354 #[test]
355 fn test_config_default() {
356 let config = DriverConfig::default();
357 assert_eq!(config.entries, 256);
358 assert!(!config.submit_wait);
359 assert_eq!(config.cpu_affinity, None);
360 assert!(config.defer_wakeup);
361 }
362}