use std::{cell::RefCell, convert::TryInto, os::unix::io::RawFd, rc::Rc, time::Duration};
use vec_map::VecMap;
use crate::{loop_logic::CalloopKey, sources::timer::TimerWheel};
#[cfg(any(target_os = "linux", target_os = "android"))]
mod epoll;
#[cfg(any(target_os = "linux", target_os = "android"))]
use epoll::Epoll as Poller;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "macos"
))]
mod kqueue;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "macos"
))]
use kqueue::Kqueue as Poller;
#[derive(Copy, Clone, Debug)]
pub enum Mode {
OneShot,
Level,
Edge,
}
#[derive(Copy, Clone, Debug)]
pub struct Interest {
pub readable: bool,
pub writable: bool,
}
impl Interest {
pub const EMPTY: Interest = Interest {
readable: false,
writable: false,
};
pub const READ: Interest = Interest {
readable: true,
writable: false,
};
pub const WRITE: Interest = Interest {
readable: false,
writable: true,
};
pub const BOTH: Interest = Interest {
readable: true,
writable: true,
};
}
#[derive(Copy, Clone, Debug)]
pub struct Readiness {
pub readable: bool,
pub writable: bool,
pub error: bool,
}
impl Readiness {
pub const EMPTY: Readiness = Readiness {
readable: false,
writable: false,
error: false,
};
}
#[derive(Debug)]
pub(crate) struct PollEvent {
pub(crate) readiness: Readiness,
pub(crate) token: Token,
}
#[derive(Debug)]
pub struct TokenFactory {
key: CalloopKey,
sub_id: u32,
}
impl TokenFactory {
pub(crate) fn new(key: CalloopKey) -> TokenFactory {
TokenFactory { key, sub_id: 0 }
}
pub fn token(&mut self) -> Token {
let token = Token {
key: self.key,
sub_id: self.sub_id,
};
self.sub_id += 1;
token
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Token {
pub(crate) key: CalloopKey,
pub(crate) sub_id: u32,
}
pub struct Poll {
poller: Poller,
tokens: VecMap<*mut Token>,
pub(crate) timers: Rc<RefCell<TimerWheel>>,
}
impl std::fmt::Debug for Poll {
#[cfg_attr(coverage, no_coverage)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Poll { ... }")
}
}
impl Poll {
pub(crate) fn new(high_precision: bool) -> crate::Result<Poll> {
Ok(Poll {
poller: Poller::new(high_precision)?,
tokens: VecMap::new(),
timers: Rc::new(RefCell::new(TimerWheel::new())),
})
}
pub(crate) fn poll(
&mut self,
mut timeout: Option<std::time::Duration>,
) -> crate::Result<Vec<PollEvent>> {
let now = std::time::Instant::now();
if let Some(next_timeout) = self.timers.borrow().next_deadline() {
if next_timeout <= now {
timeout = Some(Duration::ZERO);
} else if let Some(deadline) = timeout {
timeout = Some(std::cmp::min(deadline, next_timeout - now));
} else {
timeout = Some(next_timeout - now);
}
};
let mut events = self.poller.poll(timeout)?;
let now = std::time::Instant::now();
let mut timers = self.timers.borrow_mut();
while let Some((_, token)) = timers.next_expired(now) {
events.push(PollEvent {
readiness: Readiness {
readable: true,
writable: false,
error: false,
},
token,
});
}
Ok(events)
}
pub fn register(
&mut self,
fd: RawFd,
interest: Interest,
mode: Mode,
token: Token,
) -> crate::Result<()> {
let token_box = Box::new(token);
let token_ptr = Box::into_raw(token_box);
let registration_result = self.poller.register(fd, interest, mode, token_ptr);
if registration_result.is_err() {
let token_box = unsafe { Box::from_raw(token_ptr) };
std::mem::drop(token_box);
} else {
let index = index_from_fd(fd);
if self.tokens.insert(index, token_ptr).is_some() {
panic!("File descriptor ({}) already registered", fd);
}
}
registration_result
}
pub fn reregister(
&mut self,
fd: RawFd,
interest: Interest,
mode: Mode,
token: Token,
) -> crate::Result<()> {
let token_box = Box::new(token);
let token_ptr = Box::into_raw(token_box);
let reregistration_result = self.poller.reregister(fd, interest, mode, token_ptr);
if reregistration_result.is_err() {
let token_box = unsafe { Box::from_raw(token_ptr) };
std::mem::drop(token_box);
} else {
let index = index_from_fd(fd);
if let Some(previous) = self.tokens.insert(index, token_ptr) {
let token_box = unsafe { Box::from_raw(previous) };
std::mem::drop(token_box);
} else {
panic!("File descriptor ({}) had no previous registration", fd);
}
}
reregistration_result
}
pub fn unregister(&mut self, fd: RawFd) -> crate::Result<()> {
let unregistration_result = self.poller.unregister(fd);
if unregistration_result.is_ok() {
let index = index_from_fd(fd);
if let Some(previous) = self.tokens.remove(index) {
let token_box = unsafe { Box::from_raw(previous) };
std::mem::drop(token_box);
} else {
panic!("File descriptor ({}) had no previous registration", fd);
}
}
unregistration_result
}
}
fn index_from_fd(fd: RawFd) -> usize {
fd.try_into()
.unwrap_or_else(|_| panic!("File descriptor ({}) is invalid", fd))
}