ruyi 0.1.6

An event-driven framework for non-blocking, asynchronous I/O in Rust
Documentation
use std::io;
use std::os::unix::io::{RawFd, AsRawFd};
use std::time::Duration;
use std::ptr;

use libc;

use super::cvt;
use super::super::into_millis;

pub const OP_READ: usize = libc::EPOLLIN as usize;
pub const OP_WRITE: usize = libc::EPOLLOUT as usize;

#[repr(C)]
#[derive(Clone, Copy)]
pub struct Event {
    inner: libc::epoll_event,
}

impl Event {
    #[inline]
    fn new(ops: usize, token: usize) -> Self {
        Event {
            inner: libc::epoll_event {
                events: ops as u32,
                u64: token as u64,
            },
        }
    }

    #[inline]
    pub fn ops(&self) -> usize {
        self.inner.events as usize
    }

    #[inline]
    pub fn token(&self) -> usize {
        self.inner.u64 as usize
    }
}

#[derive(Debug)]
pub struct Selector {
    epfd: RawFd,
}

impl Selector {
    #[inline]
    pub fn new() -> io::Result<Self> {
        let res = unsafe { libc::epoll_create1(libc::EPOLL_CLOEXEC) };
        let epfd = cvt(res)?;
        Ok(Selector { epfd: epfd })
    }

    #[inline]
    pub fn select(
        &self,
        event_ptr: *mut Event,
        max_events: usize,
        timeout: Option<Duration>,
    ) -> io::Result<usize> {
        let events = event_ptr as *mut libc::epoll_event;
        let len = max_events as libc::c_int;
        let millis = match timeout {
            Some(dur) => into_millis(dur) as libc::c_int,
            None => -1,
        };
        let res = unsafe { libc::epoll_wait(self.epfd, events, len, millis) };
        let n = cvt(res)? as usize;
        Ok((n))
    }

    #[inline]
    pub fn register<O, T>(&self, fd: RawFd, interested_ops: O, token: T) -> io::Result<()>
    where
        O: Into<usize>,
        T: Into<usize>,
    {
        let mut ev = Event::new(interested_ops.into(), token.into());
        let res = unsafe { libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_ADD, fd, &mut ev.inner) };
        cvt(res).map(drop)
    }

    #[inline]
    pub fn reregister<O, T>(&self, fd: RawFd, interested_ops: O, token: T) -> io::Result<()>
    where
        O: Into<usize>,
        T: Into<usize>,
    {
        let mut ev = Event::new(interested_ops.into(), token.into());
        let res = unsafe { libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_MOD, fd, &mut ev.inner) };
        cvt(res).map(drop)
    }

    #[inline]
    pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
        let res = unsafe { libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_DEL, fd, ptr::null_mut()) };
        cvt(res).map(drop)
    }
}

impl Drop for Selector {
    #[inline]
    fn drop(&mut self) {
        let res = unsafe { libc::close(self.epfd) };
        cvt(res)
            .map(drop)
            .unwrap_or_else(|e| error!("Failed to close {:?}: {}", self, e));
    }
}

#[derive(Debug)]
pub struct Awakener {
    event_fd: RawFd,
}

impl Awakener {
    #[inline]
    pub fn new() -> io::Result<Self> {
        let res = unsafe { libc::eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK) };
        Ok(Awakener {
            event_fd: cvt(res)?,
        })
    }

    pub fn wakeup(&self) -> io::Result<()> {
        const NUM: *const u64 = &(::std::u64::MAX - 1);
        let res = unsafe { libc::write(self.event_fd, NUM as *const libc::c_void, 8) };
        if res == 8 {
            cvt(res).map(drop)
        } else {
            error!("Error to write 8 bytes to {:?}, writen: {}", self, res);
            Ok(())
        }
    }

    pub fn reset(&self) -> io::Result<()> {
        let mut data = 0u64;
        let buf = &mut data as *mut u64;
        let res = unsafe { libc::read(self.event_fd, buf as *mut libc::c_void, 8) };
        if res == 8 {
            cvt(res).map(drop)
        } else {
            error!("Error to read 8 bytes from {:?}, read: {}", self, res);
            Ok(())
        }
    }
}

impl AsRawFd for Awakener {
    #[inline]
    fn as_raw_fd(&self) -> RawFd {
        self.event_fd
    }
}

impl Drop for Awakener {
    fn drop(&mut self) {
        let res = unsafe { libc::close(self.event_fd) };
        cvt(res)
            .map(drop)
            .unwrap_or_else(|e| error!("Failed to close {:?}: {}", self, e));
    }
}