use crate::errno::Errno; use crate::Result; use libc::{self, c_int}; use std::mem; use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd}; libc_bitflags!( pub struct EpollFlags: c_int { EPOLLIN; EPOLLPRI; EPOLLOUT; EPOLLRDNORM; EPOLLRDBAND; EPOLLWRNORM; EPOLLWRBAND; EPOLLMSG; EPOLLERR; EPOLLHUP; EPOLLRDHUP; EPOLLEXCLUSIVE; #[cfg(not(target_arch = "mips"))] EPOLLWAKEUP; EPOLLONESHOT; EPOLLET; } ); #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[repr(i32)] #[non_exhaustive] pub enum EpollOp { EpollCtlAdd = libc::EPOLL_CTL_ADD, EpollCtlDel = libc::EPOLL_CTL_DEL, EpollCtlMod = libc::EPOLL_CTL_MOD, } libc_bitflags! { pub struct EpollCreateFlags: c_int { EPOLL_CLOEXEC; } } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[repr(transparent)] pub struct EpollEvent { event: libc::epoll_event, } impl EpollEvent { pub fn new(events: EpollFlags, data: u64) -> Self { EpollEvent { event: libc::epoll_event { events: events.bits() as u32, u64: data, }, } } pub fn empty() -> Self { unsafe { mem::zeroed::() } } pub fn events(&self) -> EpollFlags { EpollFlags::from_bits(self.event.events as c_int).unwrap() } pub fn data(&self) -> u64 { self.event.u64 } } /// A safe wrapper around [`epoll`](https://man7.org/linux/man-pages/man7/epoll.7.html). /// ``` /// # use nix::sys::{epoll::{Epoll, EpollEvent, EpollFlags, EpollCreateFlags}, eventfd::{eventfd, EfdFlags}}; /// # use nix::unistd::write; /// # use std::os::unix::io::{OwnedFd, FromRawFd, AsRawFd, AsFd}; /// # use std::time::{Instant, Duration}; /// # fn main() -> nix::Result<()> { /// const DATA: u64 = 17; /// const MILLIS: u64 = 100; /// /// // Create epoll /// let epoll = Epoll::new(EpollCreateFlags::empty())?; /// /// // Create eventfd & Add event /// let eventfd = eventfd(0, EfdFlags::empty())?; /// epoll.add(&eventfd, EpollEvent::new(EpollFlags::EPOLLIN,DATA))?; /// /// // Arm eventfd & Time wait /// write(eventfd.as_raw_fd(), &1u64.to_ne_bytes())?; /// let now = Instant::now(); /// /// // Wait on event /// let mut events = [EpollEvent::empty()]; /// epoll.wait(&mut events, MILLIS as isize)?; /// /// // Assert data correct & timeout didn't occur /// assert_eq!(events[0].data(), DATA); /// assert!(now.elapsed() < Duration::from_millis(MILLIS)); /// # Ok(()) /// # } /// ``` #[derive(Debug)] pub struct Epoll(pub OwnedFd); impl Epoll { /// Creates a new epoll instance and returns a file descriptor referring to that instance. /// /// [`epoll_create1`](https://man7.org/linux/man-pages/man2/epoll_create1.2.html). pub fn new(flags: EpollCreateFlags) -> Result { let res = unsafe { libc::epoll_create1(flags.bits()) }; let fd = Errno::result(res)?; let owned_fd = unsafe { OwnedFd::from_raw_fd(fd) }; Ok(Self(owned_fd)) } /// Add an entry to the interest list of the epoll file descriptor for /// specified in events. /// /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_ADD`. pub fn add(&self, fd: Fd, mut event: EpollEvent) -> Result<()> { self.epoll_ctl(EpollOp::EpollCtlAdd, fd, &mut event) } /// Remove (deregister) the target file descriptor `fd` from the interest list. /// /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_DEL` . pub fn delete(&self, fd: Fd) -> Result<()> { self.epoll_ctl(EpollOp::EpollCtlDel, fd, None) } /// Change the settings associated with `fd` in the interest list to the new settings specified /// in `event`. /// /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_MOD`. pub fn modify( &self, fd: Fd, event: &mut EpollEvent, ) -> Result<()> { self.epoll_ctl(EpollOp::EpollCtlMod, fd, event) } /// Waits for I/O events, blocking the calling thread if no events are currently available. /// (This can be thought of as fetching items from the ready list of the epoll instance.) /// /// [`epoll_wait`](https://man7.org/linux/man-pages/man2/epoll_wait.2.html) pub fn wait( &self, events: &mut [EpollEvent], timeout: isize, ) -> Result { let res = unsafe { libc::epoll_wait( self.0.as_raw_fd(), events.as_mut_ptr() as *mut libc::epoll_event, events.len() as c_int, timeout as c_int, ) }; Errno::result(res).map(|r| r as usize) } /// This system call is used to add, modify, or remove entries in the interest list of the epoll /// instance referred to by `self`. It requests that the operation `op` be performed for the /// target file descriptor, `fd`. /// /// When possible prefer [`Epoll::add`], [`Epoll::delete`] and [`Epoll::modify`]. /// /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) fn epoll_ctl<'a, Fd: AsFd, T>( &self, op: EpollOp, fd: Fd, event: T, ) -> Result<()> where T: Into>, { let event: Option<&mut EpollEvent> = event.into(); let ptr = event .map(|x| &mut x.event as *mut libc::epoll_event) .unwrap_or(std::ptr::null_mut()); unsafe { Errno::result(libc::epoll_ctl( self.0.as_raw_fd(), op as c_int, fd.as_fd().as_raw_fd(), ptr, )) .map(drop) } } } #[deprecated(since = "0.27.0", note = "Use Epoll::new() instead")] #[inline] pub fn epoll_create() -> Result { let res = unsafe { libc::epoll_create(1024) }; Errno::result(res) } #[deprecated(since = "0.27.0", note = "Use Epoll::new() instead")] #[inline] pub fn epoll_create1(flags: EpollCreateFlags) -> Result { let res = unsafe { libc::epoll_create1(flags.bits()) }; Errno::result(res) } #[deprecated(since = "0.27.0", note = "Use Epoll::epoll_ctl() instead")] #[inline] pub fn epoll_ctl<'a, T>( epfd: RawFd, op: EpollOp, fd: RawFd, event: T, ) -> Result<()> where T: Into>, { let mut event: Option<&mut EpollEvent> = event.into(); if event.is_none() && op != EpollOp::EpollCtlDel { Err(Errno::EINVAL) } else { let res = unsafe { if let Some(ref mut event) = event { libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event) } else { libc::epoll_ctl(epfd, op as c_int, fd, std::ptr::null_mut()) } }; Errno::result(res).map(drop) } } #[deprecated(since = "0.27.0", note = "Use Epoll::wait() instead")] #[inline] pub fn epoll_wait( epfd: RawFd, events: &mut [EpollEvent], timeout_ms: isize, ) -> Result { let res = unsafe { libc::epoll_wait( epfd, events.as_mut_ptr() as *mut libc::epoll_event, events.len() as c_int, timeout_ms as c_int, ) }; Errno::result(res).map(|r| r as usize) }