pub use crate::fd::{Event, Fd, Token};
use crate::CoreError;
use crate::error::syscall_ret;
use std::io::Error as IoError;
use std::os::unix::io::RawFd;
#[inline(always)]
fn errno() -> i32 {
IoError::last_os_error().raw_os_error().unwrap_or(0)
}
pub struct Reactor {
epfd: RawFd,
next_token: u64,
events_buf: Vec<libc::epoll_event>,
signalfd: Option<Fd>,
signalfd_previous_mask: Option<libc::sigset_t>,
sigchld_token: Option<Token>,
inotify_token: Option<Token>,
}
impl Reactor {
pub fn new() -> Result<Self, CoreError> {
let epfd = unsafe { libc::epoll_create1(libc::EPOLL_CLOEXEC) };
syscall_ret(epfd, "epoll_create1")?;
Ok(Self {
epfd,
next_token: 1,
events_buf: Vec::with_capacity(64),
signalfd: None,
signalfd_previous_mask: None,
sigchld_token: None,
inotify_token: None,
})
}
pub fn setup_inotify(&mut self) -> Result<(Fd, Token), CoreError> {
let fd = unsafe { libc::inotify_init1(libc::IN_CLOEXEC | libc::IN_NONBLOCK) };
syscall_ret(fd, "inotify_init1")?;
let fd_obj = Fd::new(fd, "inotify")?;
let token = self.add(&fd_obj, true, false)?;
self.inotify_token = Some(token);
Ok((fd_obj, token))
}
pub fn setup_signalfd(&mut self) -> Result<Token, CoreError> {
if self.signalfd.is_some() {
return Err(CoreError::sys(
libc::EINVAL,
"setup_signalfd already initialized",
));
}
let mut mask: libc::sigset_t = unsafe { std::mem::zeroed() };
unsafe { libc::sigemptyset(&mut mask) };
unsafe { libc::sigaddset(&mut mask, libc::SIGCHLD) };
let mut previous_mask: libc::sigset_t = unsafe { std::mem::zeroed() };
let r = unsafe { libc::pthread_sigmask(libc::SIG_BLOCK, &mask, &mut previous_mask) };
if r != 0 {
return Err(CoreError::sys(r, "pthread_sigmask(SIG_BLOCK)"));
}
let sfd = unsafe { libc::signalfd(-1, &mask, libc::SFD_NONBLOCK | libc::SFD_CLOEXEC) };
if let Err(err) = syscall_ret(sfd, "signalfd") {
let _ = unsafe {
libc::pthread_sigmask(libc::SIG_SETMASK, &previous_mask, std::ptr::null_mut())
};
return Err(err);
}
let fd = Fd::new(sfd, "signalfd")?;
let token = match self.add(&fd, true, false) {
Ok(token) => token,
Err(err) => {
let _ = unsafe {
libc::pthread_sigmask(libc::SIG_SETMASK, &previous_mask, std::ptr::null_mut())
};
return Err(err);
}
};
self.signalfd = Some(fd);
self.signalfd_previous_mask = Some(previous_mask);
self.sigchld_token = Some(token);
Ok(token)
}
pub fn drain_signalfd(&self) -> Result<(), CoreError> {
if let Some(fd) = &self.signalfd {
let mut buf = [0u8; std::mem::size_of::<libc::signalfd_siginfo>()];
loop {
match fd.read_slice(&mut buf) {
Ok(Some(n)) if n < buf.len() => break,
Ok(Some(_)) => continue,
Ok(None) => break,
Err(e) => return Err(e),
}
}
}
Ok(())
}
#[inline(always)]
pub fn add(&mut self, fd: &Fd, readable: bool, writable: bool) -> Result<Token, CoreError> {
let token = Token(self.next_token);
self.next_token += 1;
self.add_with_token(fd.raw(), token, readable, writable, false)?;
Ok(token)
}
#[inline(always)]
pub fn add_priority(&mut self, fd: &Fd) -> Result<Token, CoreError> {
let token = Token(self.next_token);
self.next_token += 1;
self.add_with_token(fd.raw(), token, false, false, true)?;
Ok(token)
}
#[inline(always)]
pub fn add_with_flags(&mut self, fd: &Fd, flags: u32) -> Result<Token, CoreError> {
let token = Token(self.next_token);
self.next_token += 1;
let mut ev = libc::epoll_event {
events: flags,
u64: token.0,
};
let r = unsafe { libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_ADD, fd.raw(), &mut ev) };
syscall_ret(r, "epoll_ctl_add")?;
Ok(token)
}
#[inline(always)]
pub(crate) fn add_with_token(
&mut self,
raw_fd: RawFd,
token: Token,
readable: bool,
writable: bool,
priority: bool,
) -> Result<(), CoreError> {
let mut events = libc::EPOLLET as u32;
if readable {
events |= libc::EPOLLIN as u32;
}
if writable {
events |= libc::EPOLLOUT as u32;
}
if priority {
events |= libc::EPOLLPRI as u32;
}
let mut ev = libc::epoll_event {
events,
u64: token.0,
};
let r = unsafe { libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_ADD, raw_fd, &mut ev) };
syscall_ret(r, "epoll_ctl_add")?;
Ok(())
}
#[inline(always)]
pub fn del(&self, fd: &Fd) -> Result<(), CoreError> {
self.del_raw(fd.raw())
}
#[inline(always)]
pub(crate) fn del_raw(&self, raw: RawFd) -> Result<(), CoreError> {
loop {
let ret = unsafe {
libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_DEL, raw, std::ptr::null_mut())
};
if ret == -1 {
let e = errno();
if e == libc::EINTR {
continue;
}
return Err(CoreError::sys(e, "epoll_ctl_del"));
}
return Ok(());
}
}
#[inline(always)]
pub fn wait(
&mut self,
buffer: &mut Vec<Event>,
max_events: usize,
timeout: i32,
) -> Result<usize, CoreError> {
buffer.clear();
if max_events == 0 {
return Ok(0);
}
if buffer.capacity() < max_events {
buffer.reserve(max_events.saturating_sub(buffer.len()));
}
if self.events_buf.capacity() < max_events {
self.events_buf
.reserve(max_events.saturating_sub(self.events_buf.len()));
}
let n = unsafe {
libc::epoll_wait(
self.epfd,
self.events_buf.as_mut_ptr(),
max_events as i32,
timeout,
)
};
if n > 0 {
unsafe {
self.events_buf.set_len(n as usize);
}
for i in 0..n as usize {
let ev = self.events_buf[i];
let is_read = (ev.events & libc::EPOLLIN as u32) != 0;
let is_priority = (ev.events & libc::EPOLLPRI as u32) != 0;
let is_write = (ev.events & libc::EPOLLOUT as u32) != 0;
let is_err = (ev.events & libc::EPOLLERR as u32) != 0;
let is_hup = (ev.events & libc::EPOLLHUP as u32) != 0;
buffer.push(Event {
token: Token(ev.u64),
readable: is_read || is_err,
priority: is_priority || is_err,
writable: is_write || is_err,
error: is_err,
hangup: is_hup,
});
}
return Ok(n as usize);
}
if n < 0 {
let e = errno();
if e == libc::EINTR {
return Ok(0);
}
return Err(CoreError::sys(e, "epoll_wait"));
}
Ok(0)
}
#[allow(dead_code)]
pub(crate) fn fd(&self) -> RawFd {
self.epfd
}
}
impl Drop for Reactor {
fn drop(&mut self) {
if let Some(mask) = self.signalfd_previous_mask.take() {
let _ =
unsafe { libc::pthread_sigmask(libc::SIG_SETMASK, &mask, std::ptr::null_mut()) };
}
if self.epfd >= 0 {
unsafe {
libc::close(self.epfd);
}
}
}
}