Skip to main content

Crate eventp

Crate eventp 

Source
Expand description

Safe Rust abstraction over Linux epoll, offering a truly zero-cost event dispatch mechanism.

Minimum supported Rust version: 1.71.0

§Platform support

Linux only, on 64-bit targets. Non-Linux and non-64-bit platforms are rejected at compile time.

Tested in CI on x86_64 and aarch64.

§Motivation

epoll allows the user to associate a custom u64 with a file descriptor (fd) when adding it. This is intended to store the address of an event context object, but in Rust, I’ve rarely seen it used correctly. Instead, it’s often used to store things like the fd itself, a token (mio), or a subscriber id (event_manager). This bloats the code, and usually introduces unnecessary overhead of several HashMap lookups.

This is often due to the challenges of safely managing ownership and fat pointers in Rust when interfacing with pointer-based C APIs. This crate aims to demonstrate how to leverage the Rust type system to handle these issues safely and with zero cost, and how to use a few tricks to wrap it all in a fluent, test-friendly API. See the Technical chapter for the principles behind this approach.

§Examples

use eventp::{tri_subscriber::WithHandler, Eventp, Subscriber};
use nix::sys::eventfd::EventFd;

fn thread_main(eventfd: EventFd) -> io::Result<()> {
    let mut reactor = Eventp::default();

    eventp::interest()                  // Create an empty interest set.
        .read()                         // Add interest in readable events.
        .with_fd(eventfd)               // (fd, interest)
        .with_handler(on_eventfd)       // (fd, interest, handler) -> a subscriber
        .register_into(&mut reactor)?;  // Register the subscriber into the reactor.

    reactor.run_forever()
}

fn on_eventfd(
    eventfd: &mut EventFd,
    // Other available parameters: Interest, Event, Pinned<'_, impl EventpOps>
) {
    let _ = eventfd.read();
    println!("eventfd triggered");
}

The handler function here supports dependency injection. That means, you can put only the parameters you need in the handler, in any order. See the tri_subscriber module for details.

Please check this full example with unit tests to learn the best practices: examples/echo-server.rs.

§Concepts

  1. The Eventp Reactor: The central event loop that manages all I/O sources.
  2. The Subscriber: A combination of an I/O source (anything that is AsFd), its event Interest (e.g., readable, writable), and a Handler function.
    • Interest vs Event: Both wrap EpollFlags. Interest is what you ask the OS to monitor (e.g., EPOLLIN). Event is what the OS reports back (e.g., EPOLLIN | EPOLLHUP). The two sets overlap but are not identical.

subscriber-eventp-relationship

§Built-in Subscribers

  • tri_subscriber: The helper subscriber constructed by the builder-like API starting from interest(), where is the recommended API entry point.
  • remote_endpoint: remote-endpoint A remote control for an Eventp instance running on another thread, allows sending closures to the Eventp thread to be executed.

§Testability and Type Hierarchy

type-hierarchy

To make unit testing easier, this crate provides MockEventp - a mock implementation based on mockall, available under the mock feature. So, it’s recommended to use the abstract EventpOps trait in the signature of handlers, which allows your code to accept both the real Eventp and MockEventp, making it highly testable.

Pinned<'_, impl EventpOps> is the handle passed to your handler when an event is triggered. It acts like &mut impl EventpOps but prevents operations that could move the underlying Eventp instance (such as std::mem::replace), thus ensuring memory safety.

The diagram also mentions EventpOpsAdd. You will rarely use this trait directly. It’s a helper trait that allows methods like register_into() to accept both types.

Re-exports§

pub use crate::mock::MockEventp;mock
pub use crate::remote_endpoint::remote_endpoint;remote-endpoint
pub use crate::subscriber::Subscriber;

Modules§

_technicaldocsrs
Technical
_technical_zhdocsrs
Technical
epoll
Re-exports of epoll related types from the nix crate.
mockmock
A mock implementation of EventpOps generated by mockall. See the mockall documentation for more information.
remote_endpointremote-endpoint
Provides a mechanism for cross-thread communication with an Eventp event loop.
subscriber
Subscriber represents types that can be registered with an Eventp to receive and handle I/O events.
thin
Thin pointer implementations.
tri_subscriber
A ternary subscriber, composed of a file descriptor, interest, and a handler.

Macros§

pinned
This macro is primarily used in tests with MockEventp to create a Pinned<'_, MockEventp>. For details on the underlying magic, see technical.

Structs§

Event
A readiness event from the I/O reactor.
Eventp
The central event loop reactor, built on top of Linux’s epoll.
Interest
A wrapper around EpollFlags, represents interest in I/O readiness events for a file descriptor.
Pinned
A deliberately narrowed view of Pin<&mut Ep> exposing only add, modify, and delete.

Traits§

EventpOps
A trait for types that can add subscribers, modify interests, and delete subscribers.
EventpOpsAdd
A helper trait that lets Subscriber::register_into accept both &mut Ep and Pinned<'_, Ep>.

Functions§

interest
Creates a new, empty Interest set. This is the recommended API entry point.