blob: f2f8c4dd597e494557c47ab09f6cdb2a390ebb7e [file] [log] [blame]
// Copyright 2015 Nathan Sizemore <[email protected]>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
#[macro_use]
extern crate bitflags;
extern crate libc;
use std::fmt::{Debug, Formatter, Result};
use std::io::{self, Error};
use std::os::unix::io::RawFd;
#[repr(i32)]
#[allow(non_camel_case_types)]
pub enum ControlOptions {
/// Indicates an addition to the interest list.
EPOLL_CTL_ADD = libc::EPOLL_CTL_ADD,
/// Indicates a modification of flags for an interest already in list.
EPOLL_CTL_MOD = libc::EPOLL_CTL_MOD,
/// Indicates a removal of an interest from the list.
EPOLL_CTL_DEL = libc::EPOLL_CTL_DEL,
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Events: u32 {
/// Sets the Edge Triggered behavior for the associated file descriptor.
///
/// The default behavior for epoll is Level Triggered.
const EPOLLET = libc::EPOLLET as u32;
/// The associated file is available for read operations.
const EPOLLIN = libc::EPOLLIN as u32;
/// Error condition happened on the associated file descriptor.
///
/// `wait` will always wait for this event; is not necessary to set it in events.
const EPOLLERR = libc::EPOLLERR as u32;
/// Hang up happened on the associated file descriptor.
///
/// `wait` will always wait for this event; it is not necessary to set it in events.
/// Note that when reading from a channel such as a pipe or a stream socket, this event
/// merely indicates that the peer closed its end of the channel. Subsequent reads from
/// the channel will return 0 (end of file) only after all outstanding data in the
/// channel has been consumed.
const EPOLLHUP = libc::EPOLLHUP as u32;
/// The associated file is available for write operations.
const EPOLLOUT = libc::EPOLLOUT as u32;
/// There is urgent data available for read operations.
const EPOLLPRI = libc::EPOLLPRI as u32;
/// Stream socket peer closed connection, or shut down writing half of connection.
///
/// This flag is especially useful for writing simple code to detect peer shutdown when
/// using Edge Triggered monitoring.
const EPOLLRDHUP = libc::EPOLLRDHUP as u32;
/// If `EPOLLONESHOT` and `EPOLLET` are clear and the process has the `CAP_BLOCK_SUSPEND`
/// capability, ensure that the system does not enter "suspend" or "hibernate" while this
/// event is pending or being processed.
///
/// The event is considered as being "processed" from the time when it is returned by
/// a call to `wait` until the next call to `wait` on the same `EpollInstance`
/// descriptor, the closure of that file descriptor, the removal of the event file
/// descriptor with `EPOLL_CTL_DEL`, or the clearing of `EPOLLWAKEUP` for the event file
/// descriptor with `EPOLL_CTL_MOD`.
const EPOLLWAKEUP = libc::EPOLLWAKEUP as u32;
/// Sets the one-shot behavior for the associated file descriptor.
///
/// This means that after an event is pulled out with `wait` the associated file
/// descriptor is internally disabled and no other events will be reported by the epoll
/// interface. The user must call `ctl` with `EPOLL_CTL_MOD` to rearm the file
/// descriptor with a new event mask.
const EPOLLONESHOT = libc::EPOLLONESHOT as u32;
/// Sets an exclusive wakeup mode for the epoll file descriptor that is being attached to
/// the target file descriptor, `fd`. When a wakeup event occurs and multiple epoll file
/// descriptors are attached to the same target file using `EPOLLEXCLUSIVE`, one or more of
/// the epoll file descriptors will receive an event with `wait`. The default in this
/// scenario (when `EPOLLEXCLUSIVE` is not set) is for all epoll file descriptors to
/// receive an event. `EPOLLEXCLUSIVE` is thus useful for avoiding thundering herd problems
/// in certain scenarios.
///
/// If the same file descriptor is in multiple epoll instances, some with the
/// `EPOLLEXCLUSIVE` flag, and others without, then events will be provided to all epoll
/// instances that did not specify `EPOLLEXCLUSIVE`, and at least one of the epoll
/// instances that did specify `EPOLLEXCLUSIVE`.
///
/// The following values may be specified in conjunction with `EPOLLEXCLUSIVE`: `EPOLLIN`,
/// `EPOLLOUT`, `EPOLLWAKEUP`, and `EPOLLET`. `EPOLLHUP` and `EPOLLERR` can also be
/// specified, but this is not required: as usual, these events are always reported if they
/// occur, regardless of whether they are specified in `Events`. Attempts to specify other
/// values in `Events` yield the error `EINVAL`.
///
/// `EPOLLEXCLUSIVE` may be used only in an `EPOLL_CTL_ADD` operation; attempts to employ
/// it with `EPOLL_CTL_MOD` yield an error. If `EPOLLEXCLUSIVE` has been set using `ctl`,
/// then a subsequent `EPOLL_CTL_MOD` on the same `epfd`, `fd` pair yields an error. A call
/// to `ctl` that specifies `EPOLLEXCLUSIVE` in `Events` and specifies the target file
/// descriptor `fd` as an epoll instance will likewise fail. The error in all of these
/// cases is `EINVAL`.
///
/// The `EPOLLEXCLUSIVE` flag is an input flag for the `Event.events` field when calling
/// `ctl`; it is never returned by `wait`.
const EPOLLEXCLUSIVE = libc::EPOLLEXCLUSIVE as u32;
}
}
/// 'libc::epoll_event' equivalent.
#[repr(C)]
#[cfg_attr(target_arch = "x86_64", repr(packed))]
#[derive(Clone, Copy)]
pub struct Event {
pub events: u32,
pub data: u64,
}
impl Event {
pub fn new(events: Events, data: u64) -> Event {
Event {
events: events.bits(),
data: data,
}
}
}
impl Debug for Event {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let data = self.data;
f.debug_struct("Event")
.field("events", &Events::from_bits_retain(self.events)) // Retain so we can see erroneously set bits too
.field("data", &data)
.finish()
}
}
/// Creates a new epoll file descriptor.
///
/// If `cloexec` is true, `FD_CLOEXEC` will be set on the returned file descriptor.
///
/// ## Notes
///
/// * `epoll_create1()` is the underlying syscall.
pub fn create(cloexec: bool) -> io::Result<RawFd> {
let flags = if cloexec { libc::EPOLL_CLOEXEC } else { 0 };
unsafe { cvt(libc::epoll_create1(flags)) }
}
/// Safe wrapper for `libc::epoll_ctl`
pub fn ctl(
epfd: RawFd,
op: ControlOptions,
fd: RawFd,
mut event: Event,
) -> io::Result<()> {
let e = &mut event as *mut _ as *mut libc::epoll_event;
unsafe { cvt(libc::epoll_ctl(epfd, op as i32, fd, e))? };
Ok(())
}
/// Safe wrapper for `libc::epoll_wait`
///
/// ## Notes
///
/// * If `timeout` is negative, it will block until an event is received.
pub fn wait(epfd: RawFd, timeout: i32, buf: &mut [Event]) -> io::Result<usize> {
let timeout = if timeout < -1 { -1 } else { timeout };
let num_events = unsafe {
cvt(libc::epoll_wait(
epfd,
buf.as_mut_ptr() as *mut libc::epoll_event,
buf.len() as i32,
timeout,
))? as usize
};
Ok(num_events)
}
/// Safe wrapper for `libc::close`
pub fn close(epfd: RawFd) -> io::Result<()> {
cvt(unsafe { libc::close(epfd) })?;
Ok(())
}
fn cvt(result: libc::c_int) -> io::Result<libc::c_int> {
if result < 0 {
Err(Error::last_os_error())
} else {
Ok(result)
}
}