Skip to content

Commit

Permalink
Epoll type
Browse files Browse the repository at this point in the history
  • Loading branch information
JonathanWoollett-Light committed Nov 29, 2022
1 parent 1aa9883 commit 671338e
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).

### Changed

- The epoll interface now uses a type.
([#1882](https://github.com/nix-rust/nix/pull/1882))
- The MSRV is now 1.63.0
([#1882](https://github.com/nix-rust/nix/pull/1882))
- The `addr` argument of `sys::mman::mmap` is now of type `Option<NonZeroUsize>`.
Expand Down
106 changes: 103 additions & 3 deletions src/sys/epoll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use crate::errno::Errno;
use crate::Result;
use libc::{self, c_int};
use std::mem;
use std::os::unix::io::RawFd;
use std::ptr;
use std::os::unix::io::{FromRawFd,RawFd, OwnedFd, AsFd, AsRawFd};

libc_bitflags!(
pub struct EpollFlags: c_int {
Expand Down Expand Up @@ -70,6 +69,107 @@ impl EpollEvent {
}
}

/// 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 = unsafe { OwnedFd::from_raw_fd(eventfd(0, EfdFlags::empty())?) };
/// epoll.add(eventfd.as_fd(), 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<Self> {
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<Fd: AsFd>(&self, fd: Fd, mut event: EpollEvent) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlAdd,fd.as_fd().as_raw_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<Fd: AsFd>(&self, fd: Fd) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlDel,fd.as_fd().as_raw_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<Fd: AsFd>(&self,fd: Fd, event: &mut EpollEvent) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlMod,fd.as_fd().as_raw_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<usize> {
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`.
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html)
pub fn epoll_ctl<'a, T>(
&self,
op: EpollOp,
fd: RawFd,
event: T,
) -> Result<()>
where
T: Into<Option<&'a mut EpollEvent>>,
{
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, ptr)).map(drop)
}
}
}

#[inline]
pub fn epoll_create() -> Result<RawFd> {
let res = unsafe { libc::epoll_create(1024) };
Expand Down Expand Up @@ -125,4 +225,4 @@ pub fn epoll_wait(
};

Errno::result(res).map(|r| r as usize)
}
}
28 changes: 25 additions & 3 deletions test/sys/test_epoll.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use nix::errno::Errno;
use nix::sys::epoll::{epoll_create1, epoll_ctl};
use nix::sys::epoll::{EpollCreateFlags, EpollEvent, EpollFlags, EpollOp};
use nix::sys::epoll::{
Epoll, EpollCreateFlags, EpollEvent, EpollFlags, EpollOp,
};

#[test]
pub fn test_epoll_errno() {
pub fn test_deprecated_epoll_errno() {
let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None);
result.expect_err("assertion failed");
Expand All @@ -15,10 +17,30 @@ pub fn test_epoll_errno() {
}

#[test]
pub fn test_epoll_ctl() {
pub fn test_deprecated_epoll_ctl() {
let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
let mut event =
EpollEvent::new(EpollFlags::EPOLLIN | EpollFlags::EPOLLERR, 1);
epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, &mut event).unwrap();
epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None).unwrap();
}


#[test]
pub fn test_epoll_errno() {
let efd = Epoll::new(EpollCreateFlags::empty()).unwrap();
let result = efd.epoll_ctl(EpollOp::EpollCtlDel, 1, None);
assert_eq!(result, Err(Errno::ENOENT));

let result = efd.epoll_ctl(EpollOp::EpollCtlAdd, 1, None);
assert_eq!(result, Err(Errno::EFAULT));
}

#[test]
pub fn test_epoll_ctl() {
let efd = Epoll::new(EpollCreateFlags::empty()).unwrap();
let mut event =
EpollEvent::new(EpollFlags::EPOLLIN | EpollFlags::EPOLLERR, 1);
efd.epoll_ctl(EpollOp::EpollCtlAdd, 1, &mut event).unwrap();
efd.epoll_ctl(EpollOp::EpollCtlDel, 1, None).unwrap();
}

0 comments on commit 671338e

Please sign in to comment.