use std::collections::HashMap;
use std::io;
use std::sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
Arc, Mutex,
};
use std::task::Waker;
use std::time::{Duration, Instant};
use super::metrics::ReactorMetrics;
use crate::{create_reactor, Event, Interest, PlatformReactor, RawFd, Reactor};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct FdKey(pub(crate) usize);
impl From<RawFd> for FdKey {
fn from(fd: RawFd) -> Self {
Self(fd as usize)
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)] pub struct FdInfo {
pub interest: Interest,
pub registered_at: Instant,
pub event_count: u64,
pub read_waker: Option<Waker>,
pub write_waker: Option<Waker>,
}
pub struct IoReactor {
pub(crate) platform_reactor: PlatformReactor,
pub(crate) running: Arc<AtomicBool>,
pub(crate) registered_fds: Arc<Mutex<HashMap<FdKey, FdInfo>>>,
pub(crate) metrics: Arc<ReactorMetrics>,
}
impl IoReactor {
pub fn new() -> io::Result<Self> {
let platform_reactor = create_reactor()?;
Ok(Self {
platform_reactor,
running: Arc::new(AtomicBool::new(false)),
registered_fds: Arc::new(Mutex::new(HashMap::new())),
metrics: Arc::new(ReactorMetrics::default()),
})
}
pub fn register_fd(&self, fd: RawFd, interest: Interest) -> io::Result<()> {
self.platform_reactor.register_fd(fd, interest)?;
let mut fds = self
.registered_fds
.lock()
.unwrap_or_else(|poison| poison.into_inner());
fds.insert(
FdKey::from(fd),
FdInfo {
interest,
registered_at: Instant::now(),
event_count: 0,
read_waker: None,
write_waker: None,
},
);
let current_count = fds.len() as u64;
self.metrics
.peak_fd_count
.fetch_max(current_count, Ordering::Relaxed);
Ok(())
}
pub fn unregister_fd(&self, fd: RawFd) -> io::Result<()> {
self.platform_reactor.unregister_fd(fd)?;
self.registered_fds
.lock()
.unwrap_or_else(|poison| poison.into_inner())
.remove(&FdKey::from(fd));
Ok(())
}
pub fn run(&self) -> io::Result<()> {
self.running.store(true, Ordering::SeqCst);
self.metrics
.start_time
.set(Instant::now())
.map_err(|_| io::Error::other("Reactor already started"))?;
while self.running.load(Ordering::SeqCst) {
self.run_iteration(Some(Duration::from_millis(10)))?;
}
Ok(())
}
pub fn run_iteration(&self, timeout: Option<Duration>) -> io::Result<()> {
let iteration_start = Instant::now();
let events = self.platform_reactor.poll_events(timeout)?;
for event in events {
self.handle_event(event)?;
}
let iteration_time = iteration_start.elapsed().as_nanos() as u64;
self.metrics
.avg_event_time_ns
.store(iteration_time, Ordering::Relaxed);
Ok(())
}
pub fn stop(&self) -> io::Result<()> {
self.running.store(false, Ordering::SeqCst);
self.platform_reactor.wake()
}
fn handle_event(&self, event: Event) -> io::Result<()> {
if let Ok(mut fds) = self.registered_fds.lock() {
if let Some(fd_info) = fds.get_mut(&FdKey::from(event.fd)) {
fd_info.event_count += 1;
}
}
self.metrics
.events_processed
.fetch_add(1, Ordering::Relaxed);
self.wake_fd_waiters(event);
Ok(())
}
fn wake_fd_waiters(&self, event: Event) {
let mut read_waker = None;
let mut write_waker = None;
if let Ok(mut fds) = self.registered_fds.lock() {
if let Some(fd_info) = fds.get_mut(&FdKey::from(event.fd)) {
if event.readable || event.error || event.hangup {
read_waker = fd_info.read_waker.take();
}
if event.writable || event.error || event.hangup {
write_waker = fd_info.write_waker.take();
}
}
}
if let Some(waker) = read_waker {
waker.wake();
}
if let Some(waker) = write_waker {
waker.wake();
}
}
pub fn register_waker(&self, fd: RawFd, interest: Interest, waker: Waker) -> io::Result<()> {
let mut fds = self
.registered_fds
.lock()
.unwrap_or_else(|poison| poison.into_inner());
if let Some(fd_info) = fds.get_mut(&FdKey::from(fd)) {
let mut new_interest = fd_info.interest;
let mut modified = false;
if interest.readable && !new_interest.readable {
new_interest.readable = true;
modified = true;
}
if interest.writable && !new_interest.writable {
new_interest.writable = true;
modified = true;
}
if modified {
self.platform_reactor.unregister_fd(fd)?;
self.platform_reactor.register_fd(fd, new_interest)?;
fd_info.interest = new_interest;
}
if interest.readable {
fd_info.read_waker = Some(waker.clone());
}
if interest.writable {
fd_info.write_waker = Some(waker);
}
Ok(())
} else {
drop(fds);
self.register_fd(fd, interest)?;
let mut fds = self
.registered_fds
.lock()
.unwrap_or_else(|poison| poison.into_inner());
let fd_info = fds
.get_mut(&FdKey::from(fd))
.expect("fd was just registered");
if interest.readable {
fd_info.read_waker = Some(waker.clone());
}
if interest.writable {
fd_info.write_waker = Some(waker);
}
Ok(())
}
}
pub fn deregister_waker(&self, fd: RawFd, interest: Interest) {
if let Ok(mut fds) = self.registered_fds.lock() {
if let Some(fd_info) = fds.get_mut(&FdKey::from(fd)) {
if interest.readable {
fd_info.read_waker = None;
}
if interest.writable {
fd_info.write_waker = None;
}
}
}
}
pub fn wake(&self) -> io::Result<()> {
self.platform_reactor.wake()
}
pub fn metrics(&self) -> ReactorMetrics {
ReactorMetrics {
events_processed: AtomicU64::new(self.metrics.events_processed.load(Ordering::Relaxed)),
avg_event_time_ns: AtomicU64::new(
self.metrics.avg_event_time_ns.load(Ordering::Relaxed),
),
peak_fd_count: AtomicU64::new(self.metrics.peak_fd_count.load(Ordering::Relaxed)),
start_time: std::sync::OnceLock::new(),
}
}
}