polling/src/epoll.rs

296 lines
8.8 KiB
Rust
Raw Normal View History

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;
use std::os::unix::io::RawFd;
use std::ptr;
use std::time::Duration;
use crate::Event;
/// 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-08-14 11:46:58 +00:00
timer_fd: RawFd,
2020-08-06 13:02:59 +00:00
}
impl Poller {
/// Creates a new poller.
pub fn new() -> io::Result<Poller> {
// According to libuv, `EPOLL_CLOEXEC` is not defined on Android API < 21.
// But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on that platform, so we use it instead.
#[cfg(target_os = "android")]
const CLOEXEC: libc::c_int = libc::O_CLOEXEC;
#[cfg(not(target_os = "android"))]
const CLOEXEC: libc::c_int = libc::EPOLL_CLOEXEC;
// Create an epoll instance.
let epoll_fd = unsafe {
// Check if the `epoll_create1` symbol is available on this platform.
let ptr = libc::dlsym(
libc::RTLD_DEFAULT,
"epoll_create1\0".as_ptr() as *const libc::c_char,
);
if ptr.is_null() {
// If not, use `epoll_create` and manually set `CLOEXEC`.
let fd = match libc::epoll_create(1024) {
-1 => return Err(io::Error::last_os_error()),
fd => fd,
};
let flags = libc::fcntl(fd, libc::F_GETFD);
libc::fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC);
fd
} else {
// Use `epoll_create1` with `CLOEXEC`.
let epoll_create1 = std::mem::transmute::<
*mut libc::c_void,
unsafe extern "C" fn(libc::c_int) -> libc::c_int,
>(ptr);
match epoll_create1(CLOEXEC) {
-1 => return Err(io::Error::last_os_error()),
fd => fd,
}
}
};
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-08-14 11:46:58 +00:00
let timer_fd = syscall!(timerfd_create(
libc::CLOCK_MONOTONIC,
libc::TFD_CLOEXEC | libc::TFD_NONBLOCK,
))?;
let poller = Poller {
epoll_fd,
event_fd,
timer_fd,
};
2020-08-06 13:02:59 +00:00
poller.insert(event_fd)?;
2020-08-14 11:46:58 +00:00
poller.insert(timer_fd)?;
2020-08-06 13:10:39 +00:00
poller.interest(
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,
},
)?;
2020-08-06 13:02:59 +00:00
2020-09-03 10:55:46 +00:00
log::trace!(
2020-08-29 14:00:59 +00:00
"new: epoll_fd={}, event_fd={}, timer_fd={}",
epoll_fd,
event_fd,
timer_fd
);
2020-08-06 13:02:59 +00:00
Ok(poller)
}
/// Inserts a file descriptor.
pub fn insert(&self, fd: RawFd) -> io::Result<()> {
2020-09-03 10:55:46 +00:00
log::trace!("insert: epoll_fd={}, fd={}", self.epoll_fd, fd);
2020-08-29 14:00:59 +00:00
2020-08-06 13:02:59 +00:00
// Put the file descriptor in non-blocking mode.
let flags = syscall!(fcntl(fd, libc::F_GETFL))?;
syscall!(fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK))?;
// Register the file descriptor in epoll.
let mut ev = libc::epoll_event {
2020-09-01 04:49:20 +00:00
events: libc::EPOLLONESHOT as _,
2020-09-01 04:48:09 +00:00
u64: crate::NOTIFY_KEY as u64,
2020-08-06 13:02:59 +00:00
};
syscall!(epoll_ctl(self.epoll_fd, libc::EPOLL_CTL_ADD, fd, &mut ev))?;
Ok(())
}
/// Sets interest in a read/write event on a file descriptor and associates a key with it.
2020-08-06 13:10:39 +00:00
pub fn interest(&self, fd: RawFd, ev: Event) -> io::Result<()> {
2020-09-03 10:55:46 +00:00
log::trace!(
2020-08-29 14:00:59 +00:00
"interest: epoll_fd={}, fd={}, ev={:?}",
self.epoll_fd,
fd,
ev
);
2020-08-06 13:02:59 +00:00
let mut flags = libc::EPOLLONESHOT;
2020-08-06 13:10:39 +00:00
if ev.readable {
2020-08-06 13:02:59 +00:00
flags |= read_flags();
}
2020-08-06 13:10:39 +00:00
if ev.writable {
2020-08-06 13:02:59 +00:00
flags |= write_flags();
}
let mut ev = libc::epoll_event {
events: flags as _,
2020-08-06 13:10:39 +00:00
u64: ev.key as u64,
2020-08-06 13:02:59 +00:00
};
syscall!(epoll_ctl(self.epoll_fd, libc::EPOLL_CTL_MOD, fd, &mut ev))?;
Ok(())
}
/// Removes a file descriptor.
pub fn remove(&self, fd: RawFd) -> io::Result<()> {
2020-09-03 10:55:46 +00:00
log::trace!("remove: epoll_fd={}, fd={}", self.epoll_fd, fd);
2020-08-29 14:00:59 +00:00
2020-08-06 13:02:59 +00:00
syscall!(epoll_ctl(
self.epoll_fd,
libc::EPOLL_CTL_DEL,
fd,
ptr::null_mut()
))?;
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<()> {
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-08-15 06:31:00 +00:00
// Configure the timeout using timerfd.
let new_val = libc::itimerspec {
it_interval: TS_ZERO,
it_value: match timeout {
None => TS_ZERO,
Some(t) => libc::timespec {
tv_sec: t.as_secs() as libc::time_t,
tv_nsec: (t.subsec_nanos() as libc::c_long).into(),
},
},
2020-08-15 06:31:00 +00:00
};
syscall!(timerfd_settime(self.timer_fd, 0, &new_val, ptr::null_mut()))?;
2020-08-14 11:46:58 +00:00
// Set interest in timerfd.
self.interest(
self.timer_fd,
Event {
2020-09-01 04:48:09 +00:00
key: crate::NOTIFY_KEY,
2020-08-14 11:46:58 +00:00
readable: true,
writable: false,
},
)?;
// Timeout in milliseconds for epoll.
2020-09-14 19:06:16 +00:00
let timeout_ms = match timeout {
None => -1,
Some(t) if t == Duration::from_secs(0) => 0,
Some(t) => {
// 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-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,
&mut buf[0] as *mut u8 as *mut libc::c_void,
buf.len()
));
2020-08-06 13:10:39 +00:00
self.interest(
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,
},
)?;
2020-08-06 13:02:59 +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,
&buf[0] as *const u8 as *const libc::c_void,
buf.len()
));
Ok(())
}
}
impl Drop for Poller {
fn drop(&mut self) {
2020-09-03 10:55:46 +00:00
log::trace!(
2020-08-29 14:00:59 +00:00
"drop: epoll_fd={}, event_fd={}, timer_fd={}",
self.epoll_fd,
self.event_fd,
self.timer_fd
);
2020-08-06 13:02:59 +00:00
let _ = self.remove(self.event_fd);
2020-08-14 11:46:58 +00:00
let _ = self.remove(self.timer_fd);
2020-08-06 13:02:59 +00:00
let _ = syscall!(close(self.event_fd));
2020-08-14 11:46:58 +00:00
let _ = syscall!(close(self.timer_fd));
2020-08-06 13:02:59 +00:00
let _ = syscall!(close(self.epoll_fd));
}
}
2020-08-14 11:46:58 +00:00
/// `timespec` value that equals zero.
const TS_ZERO: libc::timespec = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
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 {
list: Box<[libc::epoll_event]>,
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 };
let list = vec![ev; 1000].into_boxed_slice();
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,
})
}
}