2020-08-14 10:55:20 +00:00
|
|
|
//! Bindings to epoll (Linux, Android).
|
2020-08-06 13:02:59 +00:00
|
|
|
|
2020-09-14 19:06:16 +00:00
|
|
|
use std::convert::TryInto;
|
2020-08-06 13:02:59 +00:00
|
|
|
use std::io;
|
2022-08-18 16:52:28 +00:00
|
|
|
use std::os::unix::io::{AsRawFd, RawFd};
|
2020-08-06 13:02:59 +00:00
|
|
|
use std::ptr;
|
|
|
|
use std::time::Duration;
|
|
|
|
|
2022-08-18 16:52:28 +00:00
|
|
|
#[cfg(not(polling_no_io_safety))]
|
|
|
|
use std::os::unix::io::{AsFd, BorrowedFd};
|
|
|
|
|
2022-12-30 22:43:47 +00:00
|
|
|
use crate::{Event, PollMode};
|
2020-08-06 13:02:59 +00:00
|
|
|
|
|
|
|
/// Interface to epoll.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Poller {
|
|
|
|
/// File descriptor for the epoll instance.
|
|
|
|
epoll_fd: RawFd,
|
|
|
|
/// File descriptor for the eventfd that produces notifications.
|
|
|
|
event_fd: RawFd,
|
2020-08-14 14:08:59 +00:00
|
|
|
/// File descriptor for the timerfd that produces timeouts.
|
2020-09-14 19:32:28 +00:00
|
|
|
timer_fd: Option<RawFd>,
|
2020-08-06 13:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Poller {
|
|
|
|
/// Creates a new poller.
|
|
|
|
pub fn new() -> io::Result<Poller> {
|
|
|
|
// Create an epoll instance.
|
2020-08-27 18:01:38 +00:00
|
|
|
//
|
|
|
|
// Use `epoll_create1` with `EPOLL_CLOEXEC`.
|
|
|
|
let epoll_fd = syscall!(syscall(
|
|
|
|
libc::SYS_epoll_create1,
|
|
|
|
libc::EPOLL_CLOEXEC as libc::c_int
|
|
|
|
))
|
|
|
|
.map(|fd| fd as libc::c_int)
|
|
|
|
.or_else(|e| {
|
|
|
|
match e.raw_os_error() {
|
|
|
|
Some(libc::ENOSYS) => {
|
|
|
|
// If `epoll_create1` is not implemented, use `epoll_create`
|
|
|
|
// and manually set `FD_CLOEXEC`.
|
|
|
|
let fd = syscall!(epoll_create(1024))?;
|
|
|
|
|
|
|
|
if let Ok(flags) = syscall!(fcntl(fd, libc::F_GETFD)) {
|
|
|
|
let _ = syscall!(fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC));
|
|
|
|
}
|
2020-08-06 13:02:59 +00:00
|
|
|
|
2020-08-27 18:01:38 +00:00
|
|
|
Ok(fd)
|
2020-08-06 13:02:59 +00:00
|
|
|
}
|
2020-08-27 18:01:38 +00:00
|
|
|
_ => Err(e),
|
2020-08-06 13:02:59 +00:00
|
|
|
}
|
2020-08-27 18:01:38 +00:00
|
|
|
})?;
|
2020-08-06 13:02:59 +00:00
|
|
|
|
2020-08-14 11:46:58 +00:00
|
|
|
// Set up eventfd and timerfd.
|
2020-08-06 13:02:59 +00:00
|
|
|
let event_fd = syscall!(eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK))?;
|
2020-09-14 19:32:28 +00:00
|
|
|
let timer_fd = syscall!(syscall(
|
|
|
|
libc::SYS_timerfd_create,
|
|
|
|
libc::CLOCK_MONOTONIC as libc::c_int,
|
|
|
|
(libc::TFD_CLOEXEC | libc::TFD_NONBLOCK) as libc::c_int,
|
|
|
|
))
|
|
|
|
.map(|fd| fd as libc::c_int)
|
|
|
|
.ok();
|
|
|
|
|
2020-08-14 11:46:58 +00:00
|
|
|
let poller = Poller {
|
|
|
|
epoll_fd,
|
|
|
|
event_fd,
|
|
|
|
timer_fd,
|
|
|
|
};
|
2020-09-14 19:32:28 +00:00
|
|
|
|
|
|
|
if let Some(timer_fd) = timer_fd {
|
2022-12-30 22:43:47 +00:00
|
|
|
poller.add(timer_fd, Event::none(crate::NOTIFY_KEY), PollMode::Oneshot)?;
|
2020-09-14 19:32:28 +00:00
|
|
|
}
|
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
|
|
|
|
|
|
|
poller.add(
|
2020-08-06 13:10:39 +00:00
|
|
|
event_fd,
|
|
|
|
Event {
|
2020-09-01 04:48:09 +00:00
|
|
|
key: crate::NOTIFY_KEY,
|
2020-08-06 13:10:39 +00:00
|
|
|
readable: true,
|
|
|
|
writable: false,
|
|
|
|
},
|
2022-12-30 22:43:47 +00:00
|
|
|
PollMode::Oneshot,
|
2020-08-06 13:10:39 +00:00
|
|
|
)?;
|
2020-08-06 13:02:59 +00:00
|
|
|
|
2020-09-03 10:55:46 +00:00
|
|
|
log::trace!(
|
2020-09-14 19:32:28 +00:00
|
|
|
"new: epoll_fd={}, event_fd={}, timer_fd={:?}",
|
2020-08-29 14:00:59 +00:00
|
|
|
epoll_fd,
|
|
|
|
event_fd,
|
|
|
|
timer_fd
|
|
|
|
);
|
2020-08-06 13:02:59 +00:00
|
|
|
Ok(poller)
|
|
|
|
}
|
|
|
|
|
2022-12-30 22:43:47 +00:00
|
|
|
/// Whether this poller supports level-triggered events.
|
|
|
|
pub fn supports_level(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Whether the poller supports edge-triggered events.
|
|
|
|
pub fn supports_edge(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
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 new file descriptor.
|
2022-12-30 22:43:47 +00:00
|
|
|
pub fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> {
|
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
|
|
|
log::trace!("add: epoll_fd={}, fd={}, ev={:?}", self.epoll_fd, fd, ev);
|
2022-12-30 22:43:47 +00:00
|
|
|
self.ctl(libc::EPOLL_CTL_ADD, fd, Some((ev, mode)))
|
2020-08-06 13:02:59 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
/// Modifies an existing file descriptor.
|
2022-12-30 22:43:47 +00:00
|
|
|
pub fn modify(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> {
|
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
|
|
|
log::trace!("modify: epoll_fd={}, fd={}, ev={:?}", self.epoll_fd, fd, ev);
|
2022-12-30 22:43:47 +00:00
|
|
|
self.ctl(libc::EPOLL_CTL_MOD, fd, Some((ev, mode)))
|
2020-08-06 13:02:59 +00:00
|
|
|
}
|
|
|
|
|
2020-10-02 14:40:09 +00:00
|
|
|
/// Deletes a file descriptor.
|
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
|
|
|
pub fn delete(&self, fd: RawFd) -> io::Result<()> {
|
2020-09-03 10:55:46 +00:00
|
|
|
log::trace!("remove: epoll_fd={}, fd={}", self.epoll_fd, fd);
|
2020-10-02 14:40:09 +00:00
|
|
|
self.ctl(libc::EPOLL_CTL_DEL, fd, None)
|
2020-08-06 13:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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<()> {
|
2020-09-03 10:55:46 +00:00
|
|
|
log::trace!("wait: epoll_fd={}, timeout={:?}", self.epoll_fd, timeout);
|
2020-08-29 14:00:59 +00:00
|
|
|
|
2020-09-14 19:32:28 +00:00
|
|
|
if let Some(timer_fd) = self.timer_fd {
|
|
|
|
// Configure the timeout using timerfd.
|
|
|
|
let new_val = libc::itimerspec {
|
|
|
|
it_interval: TS_ZERO,
|
|
|
|
it_value: match timeout {
|
|
|
|
None => TS_ZERO,
|
2022-12-03 14:04:37 +00:00
|
|
|
Some(t) => {
|
|
|
|
let mut ts = TS_ZERO;
|
|
|
|
ts.tv_sec = t.as_secs() as libc::time_t;
|
|
|
|
ts.tv_nsec = (t.subsec_nanos() as libc::c_long).into();
|
|
|
|
ts
|
|
|
|
}
|
2020-08-15 07:05:58 +00:00
|
|
|
},
|
2020-09-14 19:32:28 +00:00
|
|
|
};
|
2020-08-15 06:31:00 +00:00
|
|
|
|
2022-12-03 14:04:37 +00:00
|
|
|
syscall!(timerfd_settime(
|
2020-09-14 19:32:28 +00:00
|
|
|
timer_fd as libc::c_int,
|
|
|
|
0 as libc::c_int,
|
|
|
|
&new_val as *const libc::itimerspec,
|
|
|
|
ptr::null_mut() as *mut libc::itimerspec
|
|
|
|
))?;
|
|
|
|
|
|
|
|
// Set interest in timerfd.
|
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
|
|
|
self.modify(
|
2020-09-14 19:32:28 +00:00
|
|
|
timer_fd,
|
|
|
|
Event {
|
|
|
|
key: crate::NOTIFY_KEY,
|
|
|
|
readable: true,
|
|
|
|
writable: false,
|
|
|
|
},
|
2022-12-30 22:43:47 +00:00
|
|
|
PollMode::Oneshot,
|
2020-09-14 19:32:28 +00:00
|
|
|
)?;
|
|
|
|
}
|
2020-08-14 11:46:58 +00:00
|
|
|
|
2020-08-15 07:05:58 +00:00
|
|
|
// Timeout in milliseconds for epoll.
|
2020-09-14 19:32:28 +00:00
|
|
|
let timeout_ms = match (self.timer_fd, timeout) {
|
|
|
|
(_, Some(t)) if t == Duration::from_secs(0) => 0,
|
|
|
|
(None, Some(t)) => {
|
2020-09-14 19:06:16 +00:00
|
|
|
// Round up to a whole millisecond.
|
|
|
|
let mut ms = t.as_millis().try_into().unwrap_or(std::i32::MAX);
|
|
|
|
if Duration::from_millis(ms as u64) < t {
|
|
|
|
ms = ms.saturating_add(1);
|
|
|
|
}
|
|
|
|
ms
|
|
|
|
}
|
2020-09-14 19:32:28 +00:00
|
|
|
_ => -1,
|
2020-08-15 07:05:58 +00:00
|
|
|
};
|
|
|
|
|
2020-08-06 13:02:59 +00:00
|
|
|
// Wait for I/O events.
|
|
|
|
let res = syscall!(epoll_wait(
|
|
|
|
self.epoll_fd,
|
|
|
|
events.list.as_mut_ptr() as *mut libc::epoll_event,
|
|
|
|
events.list.len() as libc::c_int,
|
2020-09-14 19:06:16 +00:00
|
|
|
timeout_ms as libc::c_int,
|
2020-08-06 13:02:59 +00:00
|
|
|
))?;
|
|
|
|
events.len = res as usize;
|
2020-08-29 14:00:59 +00:00
|
|
|
log::trace!("new events: epoll_fd={}, res={}", self.epoll_fd, res);
|
2020-08-06 13:02:59 +00:00
|
|
|
|
|
|
|
// Clear the notification (if received) and re-register interest in it.
|
|
|
|
let mut buf = [0u8; 8];
|
|
|
|
let _ = syscall!(read(
|
|
|
|
self.event_fd,
|
2020-10-18 22:24:56 +00:00
|
|
|
buf.as_mut_ptr() as *mut libc::c_void,
|
2020-08-06 13:02:59 +00:00
|
|
|
buf.len()
|
|
|
|
));
|
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
|
|
|
self.modify(
|
2020-08-06 13:10:39 +00:00
|
|
|
self.event_fd,
|
|
|
|
Event {
|
2020-09-01 04:48:09 +00:00
|
|
|
key: crate::NOTIFY_KEY,
|
2020-08-06 13:10:39 +00:00
|
|
|
readable: true,
|
|
|
|
writable: false,
|
|
|
|
},
|
2022-12-30 22:43:47 +00:00
|
|
|
PollMode::Oneshot,
|
2020-08-06 13:10:39 +00:00
|
|
|
)?;
|
2020-08-14 12:38:02 +00:00
|
|
|
Ok(())
|
2020-08-06 13:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Sends a notification to wake up the current or next `wait()` call.
|
|
|
|
pub fn notify(&self) -> io::Result<()> {
|
2020-09-03 10:55:46 +00:00
|
|
|
log::trace!(
|
2020-08-29 14:00:59 +00:00
|
|
|
"notify: epoll_fd={}, event_fd={}",
|
|
|
|
self.epoll_fd,
|
|
|
|
self.event_fd
|
|
|
|
);
|
|
|
|
|
2020-08-06 13:02:59 +00:00
|
|
|
let buf: [u8; 8] = 1u64.to_ne_bytes();
|
|
|
|
let _ = syscall!(write(
|
|
|
|
self.event_fd,
|
2020-10-18 22:24:56 +00:00
|
|
|
buf.as_ptr() as *const libc::c_void,
|
2020-08-06 13:02:59 +00:00
|
|
|
buf.len()
|
|
|
|
));
|
|
|
|
Ok(())
|
|
|
|
}
|
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
|
|
|
|
2020-10-02 14:40:09 +00:00
|
|
|
/// Passes arguments to `epoll_ctl`.
|
2022-12-30 22:43:47 +00:00
|
|
|
fn ctl(&self, op: libc::c_int, fd: RawFd, ev: Option<(Event, PollMode)>) -> io::Result<()> {
|
|
|
|
let mut ev = ev.map(|(ev, mode)| {
|
|
|
|
let mut flags = match mode {
|
|
|
|
PollMode::Oneshot => libc::EPOLLONESHOT,
|
|
|
|
PollMode::Level => 0,
|
|
|
|
PollMode::Edge => libc::EPOLLET,
|
2023-03-25 14:22:45 +00:00
|
|
|
PollMode::EdgeOneshot => libc::EPOLLONESHOT | libc::EPOLLET,
|
2022-12-30 22:43:47 +00:00
|
|
|
};
|
2020-10-02 14:40:09 +00:00
|
|
|
if ev.readable {
|
|
|
|
flags |= read_flags();
|
|
|
|
}
|
|
|
|
if ev.writable {
|
|
|
|
flags |= write_flags();
|
|
|
|
}
|
|
|
|
libc::epoll_event {
|
|
|
|
events: flags as _,
|
|
|
|
u64: ev.key as u64,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
syscall!(epoll_ctl(
|
|
|
|
self.epoll_fd,
|
|
|
|
op,
|
|
|
|
fd,
|
|
|
|
ev.as_mut()
|
2020-10-02 16:19:15 +00:00
|
|
|
.map(|ev| ev as *mut libc::epoll_event)
|
2020-10-02 14:40:09 +00:00
|
|
|
.unwrap_or(ptr::null_mut()),
|
|
|
|
))?;
|
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
|
|
|
Ok(())
|
|
|
|
}
|
2020-08-06 13:02:59 +00:00
|
|
|
}
|
|
|
|
|
2022-08-18 16:52:28 +00:00
|
|
|
impl AsRawFd for Poller {
|
|
|
|
fn as_raw_fd(&self) -> RawFd {
|
|
|
|
self.epoll_fd
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(polling_no_io_safety))]
|
|
|
|
impl AsFd for Poller {
|
|
|
|
fn as_fd(&self) -> BorrowedFd<'_> {
|
|
|
|
// SAFETY: lifetime is bound by "self"
|
|
|
|
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-06 13:02:59 +00:00
|
|
|
impl Drop for Poller {
|
|
|
|
fn drop(&mut self) {
|
2020-09-03 10:55:46 +00:00
|
|
|
log::trace!(
|
2020-09-14 19:32:28 +00:00
|
|
|
"drop: epoll_fd={}, event_fd={}, timer_fd={:?}",
|
2020-08-29 14:00:59 +00:00
|
|
|
self.epoll_fd,
|
|
|
|
self.event_fd,
|
|
|
|
self.timer_fd
|
|
|
|
);
|
2020-09-14 19:32:28 +00:00
|
|
|
|
|
|
|
if let Some(timer_fd) = self.timer_fd {
|
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
|
|
|
let _ = self.delete(timer_fd);
|
2020-09-14 19:32:28 +00:00
|
|
|
let _ = syscall!(close(timer_fd));
|
|
|
|
}
|
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
|
|
|
let _ = self.delete(self.event_fd);
|
2020-08-06 13:02:59 +00:00
|
|
|
let _ = syscall!(close(self.event_fd));
|
|
|
|
let _ = syscall!(close(self.epoll_fd));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-14 11:46:58 +00:00
|
|
|
/// `timespec` value that equals zero.
|
2022-12-03 14:04:37 +00:00
|
|
|
const TS_ZERO: libc::timespec =
|
|
|
|
unsafe { std::mem::transmute([0u8; std::mem::size_of::<libc::timespec>()]) };
|
2020-08-14 11:46:58 +00:00
|
|
|
|
2020-08-06 13:02:59 +00:00
|
|
|
/// Epoll flags for all possible readability events.
|
|
|
|
fn read_flags() -> libc::c_int {
|
|
|
|
libc::EPOLLIN | libc::EPOLLRDHUP | libc::EPOLLHUP | libc::EPOLLERR | libc::EPOLLPRI
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Epoll flags for all possible writability events.
|
|
|
|
fn write_flags() -> libc::c_int {
|
|
|
|
libc::EPOLLOUT | libc::EPOLLHUP | libc::EPOLLERR
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A list of reported I/O events.
|
|
|
|
pub struct Events {
|
2022-08-21 12:09:43 +00:00
|
|
|
list: Box<[libc::epoll_event; 1024]>,
|
2020-08-06 13:02:59 +00:00
|
|
|
len: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl Send for Events {}
|
|
|
|
|
|
|
|
impl Events {
|
|
|
|
/// Creates an empty list.
|
|
|
|
pub fn new() -> Events {
|
|
|
|
let ev = libc::epoll_event { events: 0, u64: 0 };
|
2022-08-21 12:09:43 +00:00
|
|
|
let list = Box::new([ev; 1024]);
|
2020-08-06 13:02:59 +00:00
|
|
|
let len = 0;
|
|
|
|
Events { list, len }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Iterates over I/O events.
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
|
|
|
|
self.list[..self.len].iter().map(|ev| Event {
|
|
|
|
key: ev.u64 as usize,
|
|
|
|
readable: (ev.events as libc::c_int & read_flags()) != 0,
|
|
|
|
writable: (ev.events as libc::c_int & write_flags()) != 0,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|