blob: 3205a613f1f2a344d169dfaef69ca4437791fa30 [file] [log] [blame]
//! epoll support.
//!
//! This is an experiment, and it isn't yet clear whether epoll is the right
//! level of abstraction at which to introduce safety. But it works fairly well
//! in simple examples 🙂.
//!
//! # Examples
//!
//! ```rust,no_run
//! # #![cfg_attr(io_lifetimes_use_std, feature(io_safety))]
//! # #[cfg(feature = "net")]
//! # fn main() -> std::io::Result<()> {
//! use io_lifetimes::AsFd;
//! use rustix::io::epoll::{self, Epoll};
//! use rustix::io::{ioctl_fionbio, read, write};
//! use rustix::net::{
//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, Protocol, SocketAddrV4,
//! SocketType,
//! };
//! use std::os::unix::io::AsRawFd;
//!
//! // Create a socket and listen on it.
//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, Protocol::default())?;
//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?;
//! listen(&listen_sock, 1)?;
//!
//! // Create an epoll object. Using `Owning` here means the epoll object will
//! // take ownership of the file descriptors registered with it.
//! let epoll = Epoll::new(epoll::CreateFlags::CLOEXEC, epoll::Owning::new())?;
//!
//! // Remember the socket raw fd, which we use for comparisons only.
//! let raw_listen_sock = listen_sock.as_fd().as_raw_fd();
//!
//! // Register the socket with the epoll object.
//! epoll.add(listen_sock, epoll::EventFlags::IN)?;
//!
//! // Process events.
//! let mut event_list = epoll::EventVec::with_capacity(4);
//! loop {
//! epoll.wait(&mut event_list, -1)?;
//! for (_event_flags, target) in &event_list {
//! if target.as_raw_fd() == raw_listen_sock {
//! // Accept a new connection, set it to non-blocking, and
//! // register to be notified when it's ready to write to.
//! let conn_sock = accept(&*target)?;
//! ioctl_fionbio(&conn_sock, true)?;
//! epoll.add(conn_sock, epoll::EventFlags::OUT | epoll::EventFlags::ET)?;
//! } else {
//! // Write a message to the stream and then unregister it.
//! write(&*target, b"hello\n")?;
//! let _ = epoll.del(target)?;
//! }
//! }
//! }
//! # }
//! # #[cfg(not(feature = "net"))]
//! # fn main() {}
//! ```
use super::super::c;
use super::super::conv::{ret, ret_owned_fd, ret_u32};
use crate::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
#[cfg(not(feature = "rustc-dep-of-std"))]
use crate::fd::{FromRawFd, IntoRawFd};
use crate::io;
use alloc::vec::Vec;
use bitflags::bitflags;
use core::convert::TryInto;
use core::fmt;
use core::marker::PhantomData;
use core::ops::Deref;
use core::ptr::{null, null_mut};
bitflags! {
/// `EPOLL_*` for use with [`Epoll::new`].
pub struct CreateFlags: c::c_int {
/// `EPOLL_CLOEXEC`
const CLOEXEC = c::EPOLL_CLOEXEC;
}
}
bitflags! {
/// `EPOLL*` for use with [`Epoll::add`].
#[derive(Default)]
pub struct EventFlags: u32 {
/// `EPOLLIN`
const IN = c::EPOLLIN as u32;
/// `EPOLLOUT`
const OUT = c::EPOLLOUT as u32;
/// `EPOLLPRI`
const PRI = c::EPOLLPRI as u32;
/// `EPOLLERR`
const ERR = c::EPOLLERR as u32;
/// `EPOLLHUP`
const HUP = c::EPOLLHUP as u32;
/// `EPOLLET`
const ET = c::EPOLLET as u32;
/// `EPOLLONESHOT`
const ONESHOT = c::EPOLLONESHOT as u32;
/// `EPOLLWAKEUP`
const WAKEUP = c::EPOLLWAKEUP as u32;
/// `EPOLLEXCLUSIVE`
#[cfg(not(target_os = "android"))]
const EXCLUSIVE = c::EPOLLEXCLUSIVE as u32;
}
}
/// A reference to a `T`.
pub struct Ref<'a, T> {
t: T,
_phantom: PhantomData<&'a T>,
}
impl<'a, T> Ref<'a, T> {
#[inline]
fn new(t: T) -> Self {
Self {
t,
_phantom: PhantomData,
}
}
#[inline]
fn consume(self) -> T {
self.t
}
}
impl<'a, T> Deref for Ref<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.t
}
}
impl<'a, T: fmt::Debug> fmt::Debug for Ref<'a, T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.t.fmt(fmt)
}
}
/// A trait for data stored within an [`Epoll`] instance.
pub trait Context {
/// The type of an element owned by this context.
type Data;
/// The type of a value used to refer to an element owned by this context.
type Target: AsFd;
/// Assume ownership of `data`, and returning a `Target`.
fn acquire<'call>(&self, data: Self::Data) -> Ref<'call, Self::Target>;
/// Encode `target` as a `u64`. The only requirement on this value is that
/// it be decodable by `decode`.
fn encode(&self, target: Ref<'_, Self::Target>) -> u64;
/// Decode `raw`, which is a value encoded by `encode`, into a `Target`.
///
/// # Safety
///
/// `raw` must be a `u64` value returned from `encode`, from the same
/// context, and within the context's lifetime.
unsafe fn decode<'call>(&self, raw: u64) -> Ref<'call, Self::Target>;
/// Release ownership of the value referred to by `target` and return it.
fn release(&self, target: Ref<'_, Self::Target>) -> Self::Data;
}
/// A type implementing [`Context`] where the `Data` type is `BorrowedFd<'a>`.
pub struct Borrowing<'a> {
_phantom: PhantomData<BorrowedFd<'a>>,
}
impl<'a> Context for Borrowing<'a> {
type Data = BorrowedFd<'a>;
type Target = BorrowedFd<'a>;
#[inline]
fn acquire<'call>(&self, data: Self::Data) -> Ref<'call, Self::Target> {
Ref::new(data)
}
#[inline]
fn encode(&self, target: Ref<'_, Self::Target>) -> u64 {
target.as_raw_fd() as u64
}
#[inline]
unsafe fn decode<'call>(&self, raw: u64) -> Ref<'call, Self::Target> {
Ref::new(BorrowedFd::<'a>::borrow_raw(raw as RawFd))
}
#[inline]
fn release(&self, target: Ref<'_, Self::Target>) -> Self::Data {
target.consume()
}
}
/// A type implementing [`Context`] where the `Data` type is `T`, a type
/// implementing `From<OwnedFd>` and `From<T> for OwnedFd`.
///
/// This may be used with [`OwnedFd`], or higher-level types like
/// [`std::fs::File`] or [`std::net::TcpStream`].
#[cfg(not(feature = "rustc-dep-of-std"))]
pub struct Owning<'context, T: Into<OwnedFd> + From<OwnedFd>> {
_phantom: PhantomData<&'context T>,
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: Into<OwnedFd> + From<OwnedFd>> Owning<'context, T> {
/// Creates a new empty `Owning`.
#[allow(clippy::new_without_default)] // This is a specialized type that doesn't need to be generically constructible.
#[inline]
pub fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> Context for Owning<'context, T> {
type Data = T;
type Target = BorrowedFd<'context>;
#[inline]
fn acquire<'call>(&self, data: Self::Data) -> Ref<'call, Self::Target> {
let fd: OwnedFd = data.into();
let raw_fd = fd.into_raw_fd();
// Safety: `epoll` will assign ownership of the file descriptor to the
// kernel epoll object. We use `Into<OwnedFd>`+`IntoRawFd` to consume
// the `Data` and extract the raw file descriptor and then "borrow" it
// with `borrow_raw` knowing that the borrow won't outlive the
// kernel epoll object.
unsafe { Ref::new(BorrowedFd::<'context>::borrow_raw(raw_fd)) }
}
#[inline]
fn encode(&self, target: Ref<'_, Self::Target>) -> u64 {
target.as_fd().as_raw_fd() as u64
}
#[inline]
unsafe fn decode<'call>(&self, raw: u64) -> Ref<'call, Self::Target> {
Ref::new(BorrowedFd::<'context>::borrow_raw(raw as RawFd))
}
#[inline]
fn release(&self, target: Ref<'_, Self::Target>) -> Self::Data {
// The file descriptor was held by the kernel epoll object and is now
// being released, so we can create a new `OwnedFd` that assumes
// ownership.
let raw_fd = target.consume().as_raw_fd();
unsafe { T::from(OwnedFd::from_raw_fd(raw_fd).into()) }
}
}
/// An "epoll", an interface to an OS object allowing one to repeatedly wait
/// for events from a set of file descriptors efficiently.
pub struct Epoll<Context: self::Context> {
epoll_fd: OwnedFd,
context: Context,
}
impl<Context: self::Context> Epoll<Context> {
/// `epoll_create1(flags)`—Creates a new `Epoll`.
///
/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file
/// descriptor from being implicitly passed across `exec` boundaries.
#[inline]
#[doc(alias = "epoll_create1")]
pub fn new(flags: CreateFlags, context: Context) -> io::Result<Self> {
// Safety: We're calling `epoll_create1` via FFI and we know how it
// behaves.
unsafe {
Ok(Self {
epoll_fd: ret_owned_fd(c::epoll_create1(flags.bits()))?,
context,
})
}
}
/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an
/// `Epoll`.
///
/// This registers interest in any of the events set in `events` occurring
/// on the file descriptor associated with `data`.
#[doc(alias = "epoll_ctl")]
pub fn add(
&self,
data: Context::Data,
event_flags: EventFlags,
) -> io::Result<Ref<'_, Context::Target>> {
// Safety: We're calling `epoll_ctl` via FFI and we know how it
// behaves.
unsafe {
let target = self.context.acquire(data);
let raw_fd = target.as_fd().as_raw_fd();
let encoded = self.context.encode(target);
ret(c::epoll_ctl(
self.epoll_fd.as_fd().as_raw_fd(),
c::EPOLL_CTL_ADD,
raw_fd,
&mut c::epoll_event {
events: event_flags.bits(),
r#u64: encoded,
},
))?;
Ok(self.context.decode(encoded))
}
}
/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in
/// this `Epoll`.
///
/// This sets the events of interest with `target` to `events`.
#[doc(alias = "epoll_ctl")]
pub fn mod_(
&self,
target: Ref<'_, Context::Target>,
event_flags: EventFlags,
) -> io::Result<()> {
let raw_fd = target.as_fd().as_raw_fd();
let encoded = self.context.encode(target);
// Safety: We're calling `epoll_ctl` via FFI and we know how it
// behaves.
unsafe {
ret(c::epoll_ctl(
self.epoll_fd.as_fd().as_raw_fd(),
c::EPOLL_CTL_MOD,
raw_fd,
&mut c::epoll_event {
events: event_flags.bits(),
r#u64: encoded,
},
))
}
}
/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in
/// this `Epoll`.
///
/// This also returns the owning `Data`.
#[doc(alias = "epoll_ctl")]
pub fn del(&self, target: Ref<'_, Context::Target>) -> io::Result<Context::Data> {
// Safety: We're calling `epoll_ctl` via FFI and we know how it
// behaves.
unsafe {
let raw_fd = target.as_fd().as_raw_fd();
ret(c::epoll_ctl(
self.epoll_fd.as_fd().as_raw_fd(),
c::EPOLL_CTL_DEL,
raw_fd,
null_mut(),
))?;
}
Ok(self.context.release(target))
}
/// `epoll_wait(self, events, timeout)`—Waits for registered events of
/// interest.
///
/// For each event of interest, an element is written to `events`. On
/// success, this returns the number of written elements.
#[doc(alias = "epoll_wait")]
pub fn wait<'context>(
&'context self,
event_list: &mut EventVec<'context, Context>,
timeout: c::c_int,
) -> io::Result<()> {
// Safety: We're calling `epoll_wait` via FFI and we know how it
// behaves.
unsafe {
event_list.events.set_len(0);
let nfds = ret_u32(c::epoll_wait(
self.epoll_fd.as_fd().as_raw_fd(),
event_list.events.as_mut_ptr().cast::<c::epoll_event>(),
event_list.events.capacity().try_into().unwrap_or(i32::MAX),
timeout,
))?;
event_list.events.set_len(nfds as usize);
event_list.context = &self.context;
}
Ok(())
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> AsRawFd for Epoll<Owning<'context, T>> {
fn as_raw_fd(&self) -> RawFd {
self.epoll_fd.as_raw_fd()
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> IntoRawFd for Epoll<Owning<'context, T>> {
fn into_raw_fd(self) -> RawFd {
self.epoll_fd.into_raw_fd()
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> FromRawFd for Epoll<Owning<'context, T>> {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self {
epoll_fd: OwnedFd::from_raw_fd(fd),
context: Owning::new(),
}
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> AsFd for Epoll<Owning<'context, T>> {
fn as_fd(&self) -> BorrowedFd<'_> {
self.epoll_fd.as_fd()
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> From<Epoll<Owning<'context, T>>>
for OwnedFd
{
fn from(epoll: Epoll<Owning<'context, T>>) -> Self {
epoll.epoll_fd
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> From<OwnedFd>
for Epoll<Owning<'context, T>>
{
fn from(fd: OwnedFd) -> Self {
Self {
epoll_fd: fd,
context: Owning::new(),
}
}
}
/// An iterator over the `Event`s in an `EventVec`.
pub struct Iter<'context, Context: self::Context> {
iter: core::slice::Iter<'context, Event>,
context: *const Context,
_phantom: PhantomData<&'context Context>,
}
impl<'context, Context: self::Context> Iterator for Iter<'context, Context> {
type Item = (EventFlags, Ref<'context, Context::Target>);
fn next(&mut self) -> Option<Self::Item> {
// Safety: `self.context` is guaranteed to be valid because we hold
// `'context` for it. And we know this event is associated with this
// context because `wait` sets both.
self.iter.next().map(|event| {
(event.event_flags, unsafe {
(*self.context).decode(event.encoded)
})
})
}
}
/// A record of an event that occurred.
#[repr(C)]
#[cfg_attr(
any(
all(
target_arch = "x86",
not(target_env = "musl"),
not(target_os = "android"),
),
target_arch = "x86_64",
),
repr(packed)
)]
struct Event {
// Match the layout of `c::epoll_event`. We just use a `u64` instead of
// the full union; `Context` implementations will simply need to deal with
// casting the value into and out of the `u64` themselves.
event_flags: EventFlags,
encoded: u64,
}
/// A vector of `Event`s, plus context for interpreting them.
pub struct EventVec<'context, Context: self::Context> {
events: Vec<Event>,
context: *const Context,
_phantom: PhantomData<&'context Context>,
}
impl<'context, Context: self::Context> EventVec<'context, Context> {
/// Constructs an `EventVec` with memory for `capacity` `Event`s.
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
events: Vec::with_capacity(capacity),
context: null(),
_phantom: PhantomData,
}
}
/// Returns the current `Event` capacity of this `EventVec`.
#[inline]
pub fn capacity(&self) -> usize {
self.events.capacity()
}
/// Reserves enough memory for at least `additional` more `Event`s.
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.events.reserve(additional);
}
/// Reserves enough memory for exactly `additional` more `Event`s.
#[inline]
pub fn reserve_exact(&mut self, additional: usize) {
self.events.reserve_exact(additional);
}
/// Clears all the `Events` out of this `EventVec`.
#[inline]
pub fn clear(&mut self) {
self.events.clear();
}
/// Shrinks the capacity of this `EventVec` as much as possible.
#[inline]
pub fn shrink_to_fit(&mut self) {
self.events.shrink_to_fit();
}
/// Returns an iterator over the `Event`s in this `EventVec`.
#[inline]
pub fn iter(&self) -> Iter<'_, Context> {
Iter {
iter: self.events.iter(),
context: self.context,
_phantom: PhantomData,
}
}
/// Returns the number of `Event`s logically contained in this `EventVec`.
#[inline]
pub fn len(&mut self) -> usize {
self.events.len()
}
/// Tests whether this `EventVec` is logically empty.
#[inline]
pub fn is_empty(&mut self) -> bool {
self.events.is_empty()
}
}
impl<'context, Context: self::Context> IntoIterator for &'context EventVec<'context, Context> {
type IntoIter = Iter<'context, Context>;
type Item = (EventFlags, Ref<'context, Context::Target>);
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}