use std::collections::HashMap;
use std::io;
use std::time::Duration;
use super::{Completion, Event, Interest, RawFd, Reactor, Token};
pub struct IoUringReactor {
max_events: usize,
registrations: HashMap<Token, Registration>,
next_token: usize,
pending: Vec<PendingOp>,
available: bool,
}
struct Registration {
fd: RawFd,
interest: Interest,
}
#[allow(dead_code)]
struct PendingOp {
token: Token,
op_type: OpType,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
enum OpType {
Read,
Write,
}
impl IoUringReactor {
pub fn new(max_events: usize) -> io::Result<Self> {
#[cfg(target_os = "linux")]
{
let available = Self::check_io_uring_support();
if !available {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
"io_uring not available on this system",
));
}
Ok(Self {
max_events,
registrations: HashMap::new(),
next_token: 1,
pending: Vec::new(),
available: true,
})
}
#[cfg(not(target_os = "linux"))]
{
let _ = max_events;
Err(io::Error::new(
io::ErrorKind::Unsupported,
"io_uring is only available on Linux",
))
}
}
#[cfg(target_os = "linux")]
fn check_io_uring_support() -> bool {
use std::fs;
if let Ok(version) = fs::read_to_string("/proc/version") {
if let Some(ver_str) = version.split_whitespace().nth(2) {
let parts: Vec<&str> = ver_str.split('.').collect();
if parts.len() >= 2 {
if let (Ok(major), Ok(minor)) =
(parts[0].parse::<u32>(), parts[1].parse::<u32>())
{
return major > 5 || (major == 5 && minor >= 1);
}
}
}
}
false
}
pub fn is_available(&self) -> bool {
self.available
}
}
impl Reactor for IoUringReactor {
fn poll(&mut self, timeout: Option<Duration>) -> io::Result<Vec<Event>> {
if !self.available {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
"io_uring not available",
));
}
let events = Vec::with_capacity(self.max_events);
if let Some(duration) = timeout {
std::thread::sleep(duration.min(Duration::from_millis(1)));
}
let _ = &self.registrations;
Ok(events)
}
fn register(&mut self, fd: RawFd, interest: Interest) -> io::Result<Token> {
let token = Token(self.next_token);
self.next_token += 1;
self.registrations
.insert(token, Registration { fd, interest });
Ok(token)
}
fn modify(&mut self, token: Token, interest: Interest) -> io::Result<()> {
if let Some(reg) = self.registrations.get_mut(&token) {
reg.interest = interest;
Ok(())
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
"Token not registered",
))
}
}
fn deregister(&mut self, token: Token) -> io::Result<()> {
self.registrations.remove(&token);
self.pending.retain(|op| op.token != token);
Ok(())
}
fn submit_read(&mut self, token: Token, _buf: &mut [u8]) -> io::Result<Option<Completion>> {
if !self.registrations.contains_key(&token) {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"Token not registered",
));
}
self.pending.push(PendingOp {
token,
op_type: OpType::Read,
});
Ok(None)
}
fn submit_write(&mut self, token: Token, _buf: &[u8]) -> io::Result<Option<Completion>> {
if !self.registrations.contains_key(&token) {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"Token not registered",
));
}
self.pending.push(PendingOp {
token,
op_type: OpType::Write,
});
Ok(None)
}
fn supports_async_io(&self) -> bool {
true
}
fn name(&self) -> &'static str {
"io_uring"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(target_os = "linux")]
fn test_iouring_creation() {
let result = IoUringReactor::new(256);
let _ = result;
}
#[test]
#[cfg(not(target_os = "linux"))]
fn test_iouring_not_available_on_non_linux() {
let result = IoUringReactor::new(256);
assert!(result.is_err());
}
}