aboutsummaryrefslogtreecommitdiff
path: root/src/sys/unix/selector/epoll.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sys/unix/selector/epoll.rs')
-rw-r--r--src/sys/unix/selector/epoll.rs235
1 files changed, 235 insertions, 0 deletions
diff --git a/src/sys/unix/selector/epoll.rs b/src/sys/unix/selector/epoll.rs
new file mode 100644
index 0000000..13f1617
--- /dev/null
+++ b/src/sys/unix/selector/epoll.rs
@@ -0,0 +1,235 @@
+use crate::{Interest, Token};
+
+use libc::{EPOLLET, EPOLLIN, EPOLLOUT, EPOLLRDHUP};
+use log::error;
+use std::os::unix::io::{AsRawFd, RawFd};
+#[cfg(debug_assertions)]
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::time::Duration;
+use std::{cmp, i32, io, ptr};
+
+/// Unique id for use as `SelectorId`.
+#[cfg(debug_assertions)]
+static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
+
+#[derive(Debug)]
+pub struct Selector {
+ #[cfg(debug_assertions)]
+ id: usize,
+ ep: RawFd,
+}
+
+impl Selector {
+ pub fn new() -> io::Result<Selector> {
+ // 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")]
+ let flag = libc::O_CLOEXEC;
+ #[cfg(not(target_os = "android"))]
+ let flag = libc::EPOLL_CLOEXEC;
+
+ syscall!(epoll_create1(flag)).map(|ep| Selector {
+ #[cfg(debug_assertions)]
+ id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
+ ep,
+ })
+ }
+
+ pub fn try_clone(&self) -> io::Result<Selector> {
+ syscall!(dup(self.ep)).map(|ep| Selector {
+ // It's the same selector, so we use the same id.
+ #[cfg(debug_assertions)]
+ id: self.id,
+ ep,
+ })
+ }
+
+ pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
+ // A bug in kernels < 2.6.37 makes timeouts larger than LONG_MAX / CONFIG_HZ
+ // (approx. 30 minutes with CONFIG_HZ=1200) effectively infinite on 32 bits
+ // architectures. The magic number is the same constant used by libuv.
+ #[cfg(target_pointer_width = "32")]
+ const MAX_SAFE_TIMEOUT: u128 = 1789569;
+ #[cfg(not(target_pointer_width = "32"))]
+ const MAX_SAFE_TIMEOUT: u128 = libc::c_int::max_value() as u128;
+
+ let timeout = timeout
+ .map(|to| cmp::min(to.as_millis(), MAX_SAFE_TIMEOUT) as libc::c_int)
+ .unwrap_or(-1);
+
+ events.clear();
+ syscall!(epoll_wait(
+ self.ep,
+ events.as_mut_ptr(),
+ events.capacity() as i32,
+ timeout,
+ ))
+ .map(|n_events| {
+ // This is safe because `epoll_wait` ensures that `n_events` are
+ // assigned.
+ unsafe { events.set_len(n_events as usize) };
+ })
+ }
+
+ pub fn register(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
+ let mut event = libc::epoll_event {
+ events: interests_to_epoll(interests),
+ u64: usize::from(token) as u64,
+ };
+
+ syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_ADD, fd, &mut event)).map(|_| ())
+ }
+
+ pub fn reregister(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
+ let mut event = libc::epoll_event {
+ events: interests_to_epoll(interests),
+ u64: usize::from(token) as u64,
+ };
+
+ syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_MOD, fd, &mut event)).map(|_| ())
+ }
+
+ pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
+ syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_DEL, fd, ptr::null_mut())).map(|_| ())
+ }
+}
+
+cfg_net! {
+ impl Selector {
+ #[cfg(debug_assertions)]
+ pub fn id(&self) -> usize {
+ self.id
+ }
+ }
+}
+
+impl AsRawFd for Selector {
+ fn as_raw_fd(&self) -> RawFd {
+ self.ep
+ }
+}
+
+impl Drop for Selector {
+ fn drop(&mut self) {
+ if let Err(err) = syscall!(close(self.ep)) {
+ error!("error closing epoll: {}", err);
+ }
+ }
+}
+
+fn interests_to_epoll(interests: Interest) -> u32 {
+ let mut kind = EPOLLET;
+
+ if interests.is_readable() {
+ kind = kind | EPOLLIN | EPOLLRDHUP;
+ }
+
+ if interests.is_writable() {
+ kind |= EPOLLOUT;
+ }
+
+ kind as u32
+}
+
+pub type Event = libc::epoll_event;
+pub type Events = Vec<Event>;
+
+pub mod event {
+ use std::fmt;
+
+ use crate::sys::Event;
+ use crate::Token;
+
+ pub fn token(event: &Event) -> Token {
+ Token(event.u64 as usize)
+ }
+
+ pub fn is_readable(event: &Event) -> bool {
+ (event.events as libc::c_int & libc::EPOLLIN) != 0
+ || (event.events as libc::c_int & libc::EPOLLPRI) != 0
+ }
+
+ pub fn is_writable(event: &Event) -> bool {
+ (event.events as libc::c_int & libc::EPOLLOUT) != 0
+ }
+
+ pub fn is_error(event: &Event) -> bool {
+ (event.events as libc::c_int & libc::EPOLLERR) != 0
+ }
+
+ pub fn is_read_closed(event: &Event) -> bool {
+ // Both halves of the socket have closed
+ event.events as libc::c_int & libc::EPOLLHUP != 0
+ // Socket has received FIN or called shutdown(SHUT_RD)
+ || (event.events as libc::c_int & libc::EPOLLIN != 0
+ && event.events as libc::c_int & libc::EPOLLRDHUP != 0)
+ }
+
+ pub fn is_write_closed(event: &Event) -> bool {
+ // Both halves of the socket have closed
+ event.events as libc::c_int & libc::EPOLLHUP != 0
+ // Unix pipe write end has closed
+ || (event.events as libc::c_int & libc::EPOLLOUT != 0
+ && event.events as libc::c_int & libc::EPOLLERR != 0)
+ // The other side (read end) of a Unix pipe has closed.
+ || event.events as libc::c_int == libc::EPOLLERR
+ }
+
+ pub fn is_priority(event: &Event) -> bool {
+ (event.events as libc::c_int & libc::EPOLLPRI) != 0
+ }
+
+ pub fn is_aio(_: &Event) -> bool {
+ // Not supported in the kernel, only in libc.
+ false
+ }
+
+ pub fn is_lio(_: &Event) -> bool {
+ // Not supported.
+ false
+ }
+
+ pub fn debug_details(f: &mut fmt::Formatter<'_>, event: &Event) -> fmt::Result {
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ fn check_events(got: &u32, want: &libc::c_int) -> bool {
+ (*got as libc::c_int & want) != 0
+ }
+ debug_detail!(
+ EventsDetails(u32),
+ check_events,
+ libc::EPOLLIN,
+ libc::EPOLLPRI,
+ libc::EPOLLOUT,
+ libc::EPOLLRDNORM,
+ libc::EPOLLRDBAND,
+ libc::EPOLLWRNORM,
+ libc::EPOLLWRBAND,
+ libc::EPOLLMSG,
+ libc::EPOLLERR,
+ libc::EPOLLHUP,
+ libc::EPOLLET,
+ libc::EPOLLRDHUP,
+ libc::EPOLLONESHOT,
+ #[cfg(any(target_os = "linux", target_os = "solaris"))]
+ libc::EPOLLEXCLUSIVE,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::EPOLLWAKEUP,
+ libc::EPOLL_CLOEXEC,
+ );
+
+ // Can't reference fields in packed structures.
+ let e_u64 = event.u64;
+ f.debug_struct("epoll_event")
+ .field("events", &EventsDetails(event.events))
+ .field("u64", &e_u64)
+ .finish()
+ }
+}
+
+#[cfg(target_os = "android")]
+#[test]
+fn assert_close_on_exec_flag() {
+ // This assertion need to be true for Selector::new.
+ assert_eq!(libc::O_CLOEXEC, libc::EPOLL_CLOEXEC);
+}