2020-08-06 20:28:38 +00:00
|
|
|
//! Bindings to event port (illumos, Solaris).
|
|
|
|
|
2023-01-13 05:35:28 +00:00
|
|
|
use std::io;
|
2023-06-11 17:37:25 +00:00
|
|
|
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
2020-08-06 20:53:33 +00:00
|
|
|
use std::time::Duration;
|
2020-08-06 20:28:38 +00:00
|
|
|
|
2023-08-04 03:15:59 +00:00
|
|
|
use rustix::event::{port, PollFlags};
|
2023-04-23 14:26:29 +00:00
|
|
|
use rustix::fd::OwnedFd;
|
2023-08-04 03:15:59 +00:00
|
|
|
use rustix::io::{fcntl_getfd, fcntl_setfd, FdFlags};
|
2023-04-23 14:26:29 +00:00
|
|
|
|
2022-12-30 22:43:47 +00:00
|
|
|
use crate::{Event, PollMode};
|
2020-08-06 20:28:38 +00:00
|
|
|
|
2020-08-06 20:53:33 +00:00
|
|
|
/// Interface to event ports.
|
2020-08-06 20:28:38 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Poller {
|
2020-08-06 20:53:33 +00:00
|
|
|
/// File descriptor for the port instance.
|
2023-04-23 14:26:29 +00:00
|
|
|
port_fd: OwnedFd,
|
2020-08-06 20:28:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Poller {
|
|
|
|
/// Creates a new poller.
|
|
|
|
pub fn new() -> io::Result<Poller> {
|
2023-04-23 14:26:29 +00:00
|
|
|
let port_fd = port::port_create()?;
|
|
|
|
let flags = fcntl_getfd(&port_fd)?;
|
|
|
|
fcntl_setfd(&port_fd, flags | FdFlags::CLOEXEC)?;
|
2020-08-06 20:28:38 +00:00
|
|
|
|
2023-06-21 03:28:18 +00:00
|
|
|
tracing::trace!(
|
|
|
|
port_fd = ?port_fd.as_raw_fd(),
|
|
|
|
"new",
|
|
|
|
);
|
|
|
|
|
2023-01-13 05:35:28 +00:00
|
|
|
Ok(Poller { port_fd })
|
2020-08-06 20:28:38 +00:00
|
|
|
}
|
|
|
|
|
2022-12-30 22:43:47 +00:00
|
|
|
/// Whether this poller supports level-triggered events.
|
|
|
|
pub fn supports_level(&self) -> bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Whether this poller supports edge-triggered events.
|
|
|
|
pub fn supports_edge(&self) -> bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
Separate adding and modifying of file descriptors
This replaces Poller.insert() and Poller.interest() with Poller.add()
and Poller.modify(), and renames Poller.remove() to Poller.delete().
The method Poller.add() is used for adding a new file descriptor, while
Poller.modify() is used for updating an existing one. Poller.remove() is
renamed to Poller.delete() so the naming scheme of these methods follows
that of epoll, wepoll, etc.
This new setup means that adding a new socket only requires a single
call of Poller.add(), instead of a combination of Poller.insert() and
Poller.interest(). This reduces the amount of system calls necessary,
and leads to a more pleasant API.
On systems that use kqueue or ports, the behaviour of Poller.add() and
Poller.modify() is the same. This is because on these systems adding an
already existing file descriptor will just update its configuration.
This however is an implementation detail and should not be relied upon
by users.
Migrating to this new API is pretty simple, simply replace this:
poller.insert(&socket);
poller.interest(&socket, event);
With this:
poller.add(&socket, event);
And for cases where Poller.interest() was used for updating an existing
file descriptor, simply replace it will a call to Poller.modify().
See https://github.com/stjepang/polling/issues/16 and
https://github.com/stjepang/polling/pull/17 for more information.
2020-10-01 19:26:44 +00:00
|
|
|
/// Adds a file descriptor.
|
2023-08-04 03:15:59 +00:00
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// The `fd` must be a valid file descriptor and it must last until it is deleted.
|
|
|
|
pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> {
|
2020-10-02 14:40:09 +00:00
|
|
|
// File descriptors don't need to be added explicitly, so just modify the interest.
|
2023-08-04 03:15:59 +00:00
|
|
|
self.modify(BorrowedFd::borrow_raw(fd), ev, mode)
|
2020-10-02 14:40:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Modifies an existing file descriptor.
|
2023-08-04 03:15:59 +00:00
|
|
|
pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> {
|
2023-06-21 03:28:18 +00:00
|
|
|
let span = tracing::trace_span!(
|
|
|
|
"modify",
|
|
|
|
port_fd = ?self.port_fd.as_raw_fd(),
|
|
|
|
?fd,
|
|
|
|
?ev,
|
|
|
|
);
|
|
|
|
let _enter = span.enter();
|
|
|
|
|
2023-04-23 14:26:29 +00:00
|
|
|
let mut flags = PollFlags::empty();
|
2020-08-06 20:28:38 +00:00
|
|
|
if ev.readable {
|
2023-04-23 14:26:29 +00:00
|
|
|
flags |= read_flags();
|
2020-08-06 20:28:38 +00:00
|
|
|
}
|
|
|
|
if ev.writable {
|
2023-04-23 14:26:29 +00:00
|
|
|
flags |= write_flags();
|
2020-08-06 20:28:38 +00:00
|
|
|
}
|
|
|
|
|
2023-03-25 14:22:45 +00:00
|
|
|
if mode != PollMode::Oneshot {
|
2022-12-30 22:43:47 +00:00
|
|
|
return Err(crate::unsupported_error(
|
|
|
|
"this kind of event is not supported with event ports",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2023-04-23 14:26:29 +00:00
|
|
|
unsafe {
|
|
|
|
port::port_associate_fd(&self.port_fd, fd, flags, ev.key as _)?;
|
|
|
|
}
|
2022-12-30 22:43:47 +00:00
|
|
|
|
2020-08-06 20:28:38 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-10-02 14:40:09 +00:00
|
|
|
/// Deletes a file descriptor.
|
2023-08-04 03:15:59 +00:00
|
|
|
pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> {
|
2023-06-21 03:28:18 +00:00
|
|
|
let span = tracing::trace_span!(
|
|
|
|
"delete",
|
|
|
|
port_fd = ?self.port_fd.as_raw_fd(),
|
|
|
|
?fd,
|
|
|
|
);
|
|
|
|
let _enter = span.enter();
|
|
|
|
|
2023-04-23 14:26:29 +00:00
|
|
|
let result = unsafe { port::port_dissociate_fd(&self.port_fd, fd) };
|
|
|
|
if let Err(e) = result {
|
|
|
|
match e {
|
|
|
|
rustix::io::Errno::NOENT => return Ok(()),
|
|
|
|
_ => return Err(e.into()),
|
2020-10-19 22:19:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-06 20:28:38 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Waits for I/O events with an optional timeout.
|
2020-08-14 12:38:02 +00:00
|
|
|
pub fn wait(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
|
2023-06-21 03:28:18 +00:00
|
|
|
let span = tracing::trace_span!(
|
|
|
|
"wait",
|
|
|
|
port_fd = ?self.port_fd.as_raw_fd(),
|
|
|
|
?timeout,
|
|
|
|
);
|
|
|
|
let _enter = span.enter();
|
|
|
|
|
2020-08-06 20:28:38 +00:00
|
|
|
// Wait for I/O events.
|
2023-04-23 14:26:29 +00:00
|
|
|
let res = port::port_getn(&self.port_fd, &mut events.list, 1, timeout);
|
2023-06-21 03:28:18 +00:00
|
|
|
tracing::trace!(
|
|
|
|
port_fd = ?self.port_fd,
|
|
|
|
res = ?events.list.len(),
|
|
|
|
"new events"
|
|
|
|
);
|
2020-08-06 20:28:38 +00:00
|
|
|
|
2020-08-10 17:16:06 +00:00
|
|
|
// Event ports sets the return value to -1 and returns ETIME on timer expire. The number of
|
|
|
|
// returned events is stored in nget, but in our case it should always be 0 since we set
|
|
|
|
// nget to 1 initially.
|
2023-04-23 14:26:29 +00:00
|
|
|
if let Err(e) = res {
|
|
|
|
match e {
|
|
|
|
rustix::io::Errno::TIME => {}
|
|
|
|
_ => return Err(e.into()),
|
|
|
|
}
|
|
|
|
}
|
2020-08-06 20:28:38 +00:00
|
|
|
|
2020-08-14 12:39:13 +00:00
|
|
|
Ok(())
|
2020-08-06 20:28:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Sends a notification to wake up the current or next `wait()` call.
|
|
|
|
pub fn notify(&self) -> io::Result<()> {
|
2023-04-23 14:26:29 +00:00
|
|
|
const PORT_SOURCE_USER: i32 = 3;
|
|
|
|
|
2023-06-21 03:28:18 +00:00
|
|
|
let span = tracing::trace_span!(
|
|
|
|
"notify",
|
|
|
|
port_fd = ?self.port_fd.as_raw_fd(),
|
|
|
|
);
|
|
|
|
let _enter = span.enter();
|
|
|
|
|
2023-01-13 05:35:28 +00:00
|
|
|
// Use port_send to send a notification to the port.
|
2023-04-23 14:26:29 +00:00
|
|
|
port::port_send(&self.port_fd, PORT_SOURCE_USER, crate::NOTIFY_KEY as _)?;
|
2023-01-13 05:35:28 +00:00
|
|
|
|
2020-08-06 20:28:38 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-18 16:52:28 +00:00
|
|
|
impl AsRawFd for Poller {
|
|
|
|
fn as_raw_fd(&self) -> RawFd {
|
2023-04-23 14:26:29 +00:00
|
|
|
self.port_fd.as_raw_fd()
|
2022-08-18 16:52:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AsFd for Poller {
|
|
|
|
fn as_fd(&self) -> BorrowedFd<'_> {
|
2023-04-23 14:26:29 +00:00
|
|
|
self.port_fd.as_fd()
|
2020-08-06 20:28:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Poll flags for all possible readability events.
|
2023-04-23 14:26:29 +00:00
|
|
|
fn read_flags() -> PollFlags {
|
|
|
|
PollFlags::IN | PollFlags::HUP | PollFlags::ERR | PollFlags::PRI
|
2020-08-06 20:28:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Poll flags for all possible writability events.
|
2023-04-23 14:26:29 +00:00
|
|
|
fn write_flags() -> PollFlags {
|
|
|
|
PollFlags::OUT | PollFlags::HUP | PollFlags::ERR
|
2020-08-06 20:28:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A list of reported I/O events.
|
|
|
|
pub struct Events {
|
2023-04-23 14:26:29 +00:00
|
|
|
list: Vec<port::Event>,
|
2020-08-06 20:28:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl Send for Events {}
|
|
|
|
|
|
|
|
impl Events {
|
|
|
|
/// Creates an empty list.
|
2023-08-14 17:03:20 +00:00
|
|
|
pub fn with_capacity(cap: usize) -> Events {
|
2023-04-23 14:26:29 +00:00
|
|
|
Events {
|
2023-08-14 17:03:20 +00:00
|
|
|
list: Vec::with_capacity(cap),
|
2023-04-23 14:26:29 +00:00
|
|
|
}
|
2020-08-06 20:28:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Iterates over I/O events.
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
|
2023-08-14 17:03:20 +00:00
|
|
|
self.list.iter().map(|ev| {
|
|
|
|
let flags = PollFlags::from_bits_truncate(ev.events() as _);
|
|
|
|
Event {
|
|
|
|
key: ev.userdata() as usize,
|
|
|
|
readable: flags.intersects(read_flags()),
|
|
|
|
writable: flags.intersects(write_flags()),
|
|
|
|
extra: EventExtra { flags },
|
|
|
|
}
|
2020-08-06 20:28:38 +00:00
|
|
|
})
|
|
|
|
}
|
2023-08-14 17:03:20 +00:00
|
|
|
|
|
|
|
/// Clear the list.
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.list.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the capacity of the list.
|
|
|
|
pub fn capacity(&self) -> usize {
|
|
|
|
self.list.capacity()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Extra information associated with an event.
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
|
|
pub struct EventExtra {
|
|
|
|
/// Flags associated with this event.
|
|
|
|
flags: PollFlags,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EventExtra {
|
|
|
|
/// Create a new, empty version of this struct.
|
|
|
|
#[inline]
|
2023-08-22 01:32:21 +00:00
|
|
|
pub const fn empty() -> EventExtra {
|
2023-08-14 17:03:20 +00:00
|
|
|
EventExtra {
|
|
|
|
flags: PollFlags::empty(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set the interrupt flag.
|
|
|
|
#[inline]
|
|
|
|
pub fn set_hup(&mut self, value: bool) {
|
|
|
|
self.flags.set(PollFlags::HUP, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set the priority flag.
|
|
|
|
#[inline]
|
|
|
|
pub fn set_pri(&mut self, value: bool) {
|
|
|
|
self.flags.set(PollFlags::PRI, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Is this an interrupt event?
|
|
|
|
#[inline]
|
|
|
|
pub fn is_hup(&self) -> bool {
|
|
|
|
self.flags.contains(PollFlags::HUP)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Is this a priority event?
|
|
|
|
#[inline]
|
|
|
|
pub fn is_pri(&self) -> bool {
|
|
|
|
self.flags.contains(PollFlags::PRI)
|
|
|
|
}
|
2024-01-26 20:58:39 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn is_connect_failed(&self) -> Option<bool> {
|
2024-03-21 05:04:46 +00:00
|
|
|
Some(self.flags.contains(PollFlags::ERR) && self.flags.contains(PollFlags::HUP))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn is_err(&self) -> Option<bool> {
|
|
|
|
Some(self.flags.contains(PollFlags::ERR))
|
2024-01-26 20:58:39 +00:00
|
|
|
}
|
2020-08-06 20:28:38 +00:00
|
|
|
}
|