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
- The
EventpReactor: The central event loop that manages all I/O sources. - The
Subscriber: A combination of an I/O source (anything that isAsFd), its eventInterest(e.g., readable, writable), and aHandlerfunction.InterestvsEvent: Both wrapEpollFlags.Interestis what you ask the OS to monitor (e.g.,EPOLLIN).Eventis what the OS reports back (e.g.,EPOLLIN | EPOLLHUP). The two sets overlap but are not identical.
§Built-in Subscribers
tri_subscriber: The helper subscriber constructed by the builder-like API starting frominterest(), where is the recommended API entry point.remote_endpoint:remote-endpointA remote control for anEventpinstance running on another thread, allows sending closures to theEventpthread to be executed.
§Testability and 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;mockpub use crate::remote_endpoint::remote_endpoint;remote-endpointpub use crate::subscriber::Subscriber;
Modules§
- _technical
docsrs - Technical
- _technical_
zh docsrs - Technical
- epoll
- Re-exports of epoll related types from the
nixcrate. - mock
mock - A mock implementation of
EventpOpsgenerated by mockall. See the mockall documentation for more information. - remote_
endpoint remote-endpoint - Provides a mechanism for cross-thread communication with an
Eventpevent loop. - subscriber
Subscriberrepresents types that can be registered with anEventpto 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 onlyadd,modify, anddelete.
Traits§
- Eventp
Ops - A trait for types that can add subscribers, modify interests, and delete subscribers.
- Eventp
OpsAdd - A helper trait that lets
Subscriber::register_intoaccept both&mut EpandPinned<'_, Ep>.