From c700ad7543b8e8982c3b1434cddb3cd69e33e980 Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Mon, 26 Aug 2024 11:18:40 -0700 Subject: [PATCH] Unify epoll backend implementations (#1131) * Unify epoll backend implementations Signed-off-by: Alex Saveau * Use new module prefix style Signed-off-by: Alex Saveau --------- Signed-off-by: Alex Saveau --- src/backend/libc/event/epoll.rs | 473 +----------------------- src/backend/libc/event/syscalls.rs | 99 ++++- src/backend/linux_raw/event/epoll.rs | 438 +--------------------- src/backend/linux_raw/event/syscalls.rs | 81 ++-- src/event/epoll.rs | 444 ++++++++++++++++++++++ src/event/mod.rs | 4 +- 6 files changed, 583 insertions(+), 956 deletions(-) create mode 100644 src/event/epoll.rs diff --git a/src/backend/libc/event/epoll.rs b/src/backend/libc/event/epoll.rs index ac0b4ca70..7473f6857 100644 --- a/src/backend/libc/event/epoll.rs +++ b/src/backend/libc/event/epoll.rs @@ -1,91 +1,10 @@ -//! Linux `epoll` support. -//! -//! # Examples -//! -//! ```no_run -//! # #[cfg(feature = "net")] -//! # fn main() -> std::io::Result<()> { -//! use rustix::event::epoll; -//! use rustix::fd::AsFd; -//! use rustix::io::{ioctl_fionbio, read, write}; -//! use rustix::net::{ -//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, SocketAddrV4, SocketType, -//! }; -//! use std::collections::HashMap; -//! use std::os::unix::io::AsRawFd; -//! -//! // Create a socket and listen on it. -//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, None)?; -//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?; -//! listen(&listen_sock, 1)?; -//! -//! // Create an epoll object. Using `Owning` here means the epoll object will -//! // take ownership of the file descriptors registered with it. -//! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?; -//! -//! // Register the socket with the epoll object. -//! epoll::add( -//! &epoll, -//! &listen_sock, -//! epoll::EventData::new_u64(1), -//! epoll::EventFlags::IN, -//! )?; -//! -//! // Keep track of the sockets we've opened. -//! let mut next_id = epoll::EventData::new_u64(2); -//! let mut sockets = HashMap::new(); -//! -//! // Process events. -//! let mut event_list = epoll::EventVec::with_capacity(4); -//! loop { -//! epoll::wait(&epoll, &mut event_list, -1)?; -//! for event in &event_list { -//! let target = event.data; -//! if target.u64() == 1 { -//! // Accept a new connection, set it to non-blocking, and -//! // register to be notified when it's ready to write to. -//! let conn_sock = accept(&listen_sock)?; -//! ioctl_fionbio(&conn_sock, true)?; -//! epoll::add( -//! &epoll, -//! &conn_sock, -//! next_id, -//! epoll::EventFlags::OUT | epoll::EventFlags::ET, -//! )?; -//! -//! // Keep track of the socket. -//! sockets.insert(next_id, conn_sock); -//! next_id = epoll::EventData::new_u64(next_id.u64() + 1); -//! } else { -//! // Write a message to the stream and then unregister it. -//! let target = sockets.remove(&target).unwrap(); -//! write(&target, b"hello\n")?; -//! let _ = epoll::delete(&epoll, &target)?; -//! } -//! } -//! } -//! # } -//! # #[cfg(not(feature = "net"))] -//! # fn main() {} -//! ``` - use crate::backend::c; -#[cfg(feature = "alloc")] -use crate::backend::conv::ret_u32; -use crate::backend::conv::{ret, ret_owned_fd}; -use crate::fd::{AsFd, AsRawFd, OwnedFd}; -use crate::io; -use crate::utils::as_mut_ptr; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; use bitflags::bitflags; -use core::ffi::c_void; -use core::hash::{Hash, Hasher}; -use core::ptr::null_mut; -use core::slice; bitflags! { - /// `EPOLL_*` for use with [`create`]. + /// `EPOLL_*` for use with [`epoll::create`]. + /// + /// [`epoll::create`]: crate::event::epoll::create #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: u32 { @@ -98,7 +17,9 @@ bitflags! { } bitflags! { - /// `EPOLL*` for use with [`add`]. + /// `EPOLL*` for use with [`epoll::add`]. + /// + /// [`epoll::add`]: crate::event::epoll::add #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct EventFlags: u32 { @@ -152,385 +73,3 @@ bitflags! { const _ = !0; } } - -/// `epoll_create1(flags)`—Creates a new epoll object. -/// -/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file -/// descriptor from being implicitly passed across `exec` boundaries. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_create.2.html -#[inline] -#[doc(alias = "epoll_create1")] -pub fn create(flags: CreateFlags) -> io::Result { - // SAFETY: We're calling `epoll_create1` via FFI and we know how it - // behaves. - unsafe { ret_owned_fd(c::epoll_create1(bitflags_bits!(flags))) } -} - -/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll -/// object. -/// -/// This registers interest in any of the events set in `events` occurring on -/// the file descriptor associated with `data`. -/// -/// Note that `close`ing a file descriptor does not necessarily unregister -/// interest which can lead to spurious events being returned from [`wait`]. If -/// a file descriptor is an `Arc`, then `epoll` can be -/// thought to maintain a `Weak` to the file descriptor. -/// Check the [faq] for details. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html -/// [faq]: https://man7.org/linux/man-pages/man7/epoll.7.html#:~:text=Will%20closing%20a%20file%20descriptor%20cause%20it%20to%20be%20removed%20from%20all%0A%20%20%20%20%20%20%20%20%20%20epoll%20interest%20lists%3F -#[doc(alias = "epoll_ctl")] -pub fn add( - epoll: impl AsFd, - source: impl AsFd, - data: EventData, - event_flags: EventFlags, -) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. We use our own `Event` struct instead of libc's because - // ours preserves pointer provenance instead of just using a `u64`, - // and we have tests elsewhere for layout equivalence. - unsafe { - let raw_fd = source.as_fd().as_raw_fd(); - ret(c::epoll_ctl( - epoll.as_fd().as_raw_fd(), - c::EPOLL_CTL_ADD, - raw_fd, - as_mut_ptr(&mut Event { - flags: event_flags, - data, - #[cfg(target_os = "redox")] - _pad: 0, - }) - .cast(), - )) - } -} - -/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in a -/// given epoll object. -/// -/// This sets the events of interest with `target` to `events`. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html -#[doc(alias = "epoll_ctl")] -pub fn modify( - epoll: impl AsFd, - source: impl AsFd, - data: EventData, - event_flags: EventFlags, -) -> io::Result<()> { - let raw_fd = source.as_fd().as_raw_fd(); - - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. We use our own `Event` struct instead of libc's because - // ours preserves pointer provenance instead of just using a `u64`, - // and we have tests elsewhere for layout equivalence. - unsafe { - ret(c::epoll_ctl( - epoll.as_fd().as_raw_fd(), - c::EPOLL_CTL_MOD, - raw_fd, - as_mut_ptr(&mut Event { - flags: event_flags, - data, - #[cfg(target_os = "redox")] - _pad: 0, - }) - .cast(), - )) - } -} - -/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a -/// given epoll object. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html -#[doc(alias = "epoll_ctl")] -pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. - unsafe { - let raw_fd = source.as_fd().as_raw_fd(); - ret(c::epoll_ctl( - epoll.as_fd().as_raw_fd(), - c::EPOLL_CTL_DEL, - raw_fd, - null_mut(), - )) - } -} - -/// `epoll_wait(self, events, timeout)`—Waits for registered events of -/// interest. -/// -/// For each event of interest, an element is written to `events`. On -/// success, this returns the number of written elements. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html -#[cfg(feature = "alloc")] -#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] -pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { - // SAFETY: We're calling `epoll_wait` via FFI and we know how it - // behaves. - unsafe { - event_list.events.set_len(0); - let nfds = ret_u32(c::epoll_wait( - epoll.as_fd().as_raw_fd(), - event_list.events.as_mut_ptr().cast::(), - event_list.events.capacity().try_into().unwrap_or(i32::MAX), - timeout, - ))?; - event_list.events.set_len(nfds as usize); - } - - Ok(()) -} - -/// An iterator over the `Event`s in an `EventVec`. -pub struct Iter<'a> { - /// Use `Copied` to copy the struct, since `Event` is `packed` on some - /// platforms, and it's common for users to directly destructure it, which - /// would lead to errors about forming references to packed fields. - iter: core::iter::Copied>, -} - -impl<'a> Iterator for Iter<'a> { - type Item = Event; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -/// A record of an event that occurred. -#[repr(C)] -#[cfg_attr( - all( - linux_kernel, - any( - all( - target_arch = "x86", - not(target_env = "musl"), - not(target_os = "android"), - ), - target_arch = "x86_64", - ) - ), - repr(packed) -)] -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct Event { - /// Which specific event(s) occurred. - pub flags: EventFlags, - /// User data. - pub data: EventData, - - #[cfg(target_os = "redox")] - _pad: u64, -} - -/// Data associated with an [`Event`]. This can either be a 64-bit integer -/// value or a pointer which preserves pointer provenance. -#[repr(C)] -#[derive(Copy, Clone)] -pub union EventData { - /// A 64-bit integer value. - as_u64: u64, - - /// A `*mut c_void` which preserves pointer provenance, extended to be - /// 64-bit so that if we read the value as a `u64` union field, we don't - /// get uninitialized memory. - sixty_four_bit_pointer: SixtyFourBitPointer, -} - -impl EventData { - /// Construct a new value containing a `u64`. - #[inline] - pub const fn new_u64(value: u64) -> Self { - Self { as_u64: value } - } - - /// Construct a new value containing a `*mut c_void`. - #[inline] - pub const fn new_ptr(value: *mut c_void) -> Self { - Self { - sixty_four_bit_pointer: SixtyFourBitPointer { - pointer: value, - #[cfg(target_pointer_width = "32")] - _padding: 0, - }, - } - } - - /// Return the value as a `u64`. - /// - /// If the stored value was a pointer, the pointer is zero-extended to a - /// `u64`. - #[inline] - pub fn u64(self) -> u64 { - unsafe { self.as_u64 } - } - - /// Return the value as a `*mut c_void`. - /// - /// If the stored value was a `u64`, the least-significant bits of the - /// `u64` are returned as a pointer value. - #[inline] - pub fn ptr(self) -> *mut c_void { - unsafe { self.sixty_four_bit_pointer.pointer } - } -} - -impl PartialEq for EventData { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.u64() == other.u64() - } -} - -impl Eq for EventData {} - -impl Hash for EventData { - #[inline] - fn hash(&self, state: &mut H) { - self.u64().hash(state) - } -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct SixtyFourBitPointer { - #[cfg(target_endian = "big")] - #[cfg(target_pointer_width = "32")] - _padding: u32, - - pointer: *mut c_void, - - #[cfg(target_endian = "little")] - #[cfg(target_pointer_width = "32")] - _padding: u32, -} - -/// A vector of `Event`s, plus context for interpreting them. -#[cfg(feature = "alloc")] -pub struct EventVec { - events: Vec, -} - -#[cfg(feature = "alloc")] -impl EventVec { - /// Constructs an `EventVec` from raw pointer, length, and capacity. - /// - /// # Safety - /// - /// This function calls [`Vec::from_raw_parts`] with its arguments. - /// - /// [`Vec::from_raw_parts`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts - #[inline] - pub unsafe fn from_raw_parts(ptr: *mut Event, len: usize, capacity: usize) -> Self { - Self { - events: Vec::from_raw_parts(ptr, len, capacity), - } - } - - /// Constructs an `EventVec` with memory for `capacity` `Event`s. - #[inline] - pub fn with_capacity(capacity: usize) -> Self { - Self { - events: Vec::with_capacity(capacity), - } - } - - /// Returns the current `Event` capacity of this `EventVec`. - #[inline] - pub fn capacity(&self) -> usize { - self.events.capacity() - } - - /// Reserves enough memory for at least `additional` more `Event`s. - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.events.reserve(additional); - } - - /// Reserves enough memory for exactly `additional` more `Event`s. - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.events.reserve_exact(additional); - } - - /// Clears all the `Events` out of this `EventVec`. - #[inline] - pub fn clear(&mut self) { - self.events.clear(); - } - - /// Shrinks the capacity of this `EventVec` as much as possible. - #[inline] - pub fn shrink_to_fit(&mut self) { - self.events.shrink_to_fit(); - } - - /// Returns an iterator over the `Event`s in this `EventVec`. - #[inline] - pub fn iter(&self) -> Iter<'_> { - Iter { - iter: self.events.iter().copied(), - } - } - - /// Returns the number of `Event`s logically contained in this `EventVec`. - #[inline] - pub fn len(&mut self) -> usize { - self.events.len() - } - - /// Tests whether this `EventVec` is logically empty. - #[inline] - pub fn is_empty(&mut self) -> bool { - self.events.is_empty() - } -} - -#[cfg(feature = "alloc")] -impl<'a> IntoIterator for &'a EventVec { - type IntoIter = Iter<'a>; - type Item = Event; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -#[test] -fn test_epoll_layouts() { - check_renamed_type!(Event, epoll_event); - check_renamed_type!(Event, epoll_event); - check_renamed_struct_renamed_field!(Event, epoll_event, flags, events); - check_renamed_struct_renamed_field!(Event, epoll_event, data, u64); -} diff --git a/src/backend/libc/event/syscalls.rs b/src/backend/libc/event/syscalls.rs index 602979256..23cb93085 100644 --- a/src/backend/libc/event/syscalls.rs +++ b/src/backend/libc/event/syscalls.rs @@ -1,27 +1,24 @@ //! libc syscalls supporting `rustix::event`. use crate::backend::c; -use crate::backend::conv::ret_c_int; -#[cfg(any(apple, netbsdlike, target_os = "dragonfly", target_os = "solaris"))] -use crate::backend::conv::ret_owned_fd; -use crate::event::PollFd; -#[cfg(any(linux_kernel, bsd, solarish, target_os = "espidf"))] -use crate::fd::OwnedFd; -use crate::io; -#[cfg(any(all(feature = "alloc", bsd), solarish))] -use {crate::backend::conv::borrowed_fd, crate::fd::BorrowedFd, core::mem::MaybeUninit}; +#[cfg(any(linux_kernel, target_os = "redox"))] +use crate::backend::conv::ret_u32; +use crate::backend::conv::{ret, ret_c_int, ret_owned_fd}; #[cfg(solarish)] -use { - crate::backend::conv::ret, crate::event::port::Event, crate::utils::as_mut_ptr, - core::ptr::null_mut, -}; +use crate::event::port::Event; #[cfg(any( linux_kernel, target_os = "freebsd", target_os = "illumos", target_os = "espidf" ))] -use {crate::backend::conv::ret_owned_fd, crate::event::EventfdFlags}; +use crate::event::EventfdFlags; +use crate::event::PollFd; +use crate::fd::OwnedFd; +use crate::io; +use crate::utils::as_mut_ptr; +use core::ptr::null_mut; +use {crate::backend::conv::borrowed_fd, crate::fd::BorrowedFd, core::mem::MaybeUninit}; #[cfg(all(feature = "alloc", bsd))] use {crate::event::kqueue::Event, crate::utils::as_ptr, core::ptr::null}; @@ -189,3 +186,77 @@ pub(crate) fn pause() { debug_assert_eq!(r, -1); debug_assert_eq!(errno, libc::EINTR); } + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_create(flags: super::epoll::CreateFlags) -> io::Result { + unsafe { ret_owned_fd(c::epoll_create1(bitflags_bits!(flags))) } +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_add( + epoll: BorrowedFd<'_>, + source: BorrowedFd, + event: &mut crate::event::epoll::Event, +) -> io::Result<()> { + // We use our own `Event` struct instead of libc's because + // ours preserves pointer provenance instead of just using a `u64`, + // and we have tests elsewhere for layout equivalence. + unsafe { + ret(c::epoll_ctl( + borrowed_fd(epoll), + c::EPOLL_CTL_ADD, + borrowed_fd(source), + as_mut_ptr(event).cast(), + )) + } +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_mod( + epoll: BorrowedFd<'_>, + source: BorrowedFd, + event: &mut crate::event::epoll::Event, +) -> io::Result<()> { + unsafe { + ret(c::epoll_ctl( + borrowed_fd(epoll), + c::EPOLL_CTL_MOD, + borrowed_fd(source), + as_mut_ptr(event).cast(), + )) + } +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_del(epoll: BorrowedFd<'_>, source: BorrowedFd) -> io::Result<()> { + unsafe { + ret(c::epoll_ctl( + borrowed_fd(epoll), + c::EPOLL_CTL_DEL, + borrowed_fd(source), + null_mut(), + )) + } +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_wait( + epoll: BorrowedFd<'_>, + events: &mut [MaybeUninit], + timeout: c::c_int, +) -> io::Result { + unsafe { + ret_u32(c::epoll_wait( + borrowed_fd(epoll), + events.as_mut_ptr().cast::(), + events.len().try_into().unwrap_or(i32::MAX), + timeout, + )) + .map(|i| i.try_into().unwrap_or(usize::MAX)) + } +} diff --git a/src/backend/linux_raw/event/epoll.rs b/src/backend/linux_raw/event/epoll.rs index 4b7f041a3..fa3a46660 100644 --- a/src/backend/linux_raw/event/epoll.rs +++ b/src/backend/linux_raw/event/epoll.rs @@ -1,89 +1,10 @@ -//! Linux `epoll` support. -//! -//! # Examples -//! -//! ```no_run -//! # #[cfg(feature = "net")] -//! # fn main() -> std::io::Result<()> { -//! use rustix::event::epoll; -//! use rustix::fd::AsFd; -//! use rustix::io::{ioctl_fionbio, read, write}; -//! use rustix::net::{ -//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, SocketAddrV4, SocketType, -//! }; -//! use std::collections::HashMap; -//! use std::os::unix::io::AsRawFd; -//! -//! // Create a socket and listen on it. -//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, None)?; -//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?; -//! listen(&listen_sock, 1)?; -//! -//! // Create an epoll object. Using `Owning` here means the epoll object will -//! // take ownership of the file descriptors registered with it. -//! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?; -//! -//! // Register the socket with the epoll object. -//! epoll::add( -//! &epoll, -//! &listen_sock, -//! epoll::EventData::new_u64(1), -//! epoll::EventFlags::IN, -//! )?; -//! -//! // Keep track of the sockets we've opened. -//! let mut next_id = epoll::EventData::new_u64(2); -//! let mut sockets = HashMap::new(); -//! -//! // Process events. -//! let mut event_list = epoll::EventVec::with_capacity(4); -//! loop { -//! epoll::wait(&epoll, &mut event_list, -1)?; -//! for event in &event_list { -//! let target = event.data; -//! if target.u64() == 1 { -//! // Accept a new connection, set it to non-blocking, and -//! // register to be notified when it's ready to write to. -//! let conn_sock = accept(&listen_sock)?; -//! ioctl_fionbio(&conn_sock, true)?; -//! epoll::add( -//! &epoll, -//! &conn_sock, -//! next_id, -//! epoll::EventFlags::OUT | epoll::EventFlags::ET, -//! )?; -//! -//! // Keep track of the socket. -//! sockets.insert(next_id, conn_sock); -//! next_id = epoll::EventData::new_u64(next_id.u64() + 1); -//! } else { -//! // Write a message to the stream and then unregister it. -//! let target = sockets.remove(&target).unwrap(); -//! write(&target, b"hello\n")?; -//! let _ = epoll::delete(&epoll, &target)?; -//! } -//! } -//! } -//! # } -//! # #[cfg(not(feature = "net"))] -//! # fn main() {} -//! ``` - -#![allow(unsafe_code)] - use crate::backend::c; -use crate::backend::event::syscalls; -use crate::fd::{AsFd, AsRawFd, OwnedFd}; -use crate::io; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; use bitflags::bitflags; -use core::ffi::c_void; -use core::hash::{Hash, Hasher}; -use core::slice; bitflags! { - /// `EPOLL_*` for use with [`create`]. + /// `EPOLL_*` for use with [`epoll::create`]. + /// + /// [`epoll::create`]: crate::event::epoll::create #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: c::c_uint { @@ -96,7 +17,9 @@ bitflags! { } bitflags! { - /// `EPOLL*` for use with [`add`]. + /// `EPOLL*` for use with [`epoll::add`]. + /// + /// [`epoll::add`]: crate::event::epoll::add #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct EventFlags: u32 { @@ -149,352 +72,3 @@ bitflags! { const _ = !0; } } - -/// `epoll_create1(flags)`—Creates a new epoll object. -/// -/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file -/// descriptor from being implicitly passed across `exec` boundaries. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_create.2.html -#[inline] -#[doc(alias = "epoll_create1")] -pub fn create(flags: CreateFlags) -> io::Result { - syscalls::epoll_create(flags) -} - -/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll -/// object. -/// -/// This registers interest in any of the events set in `events` occurring on -/// the file descriptor associated with `data`. -/// -/// Note that `close`ing a file descriptor does not necessarily unregister -/// interest which can lead to spurious events being returned from [`wait`]. If -/// a file descriptor is an `Arc`, then `epoll` can be -/// thought to maintain a `Weak` to the file descriptor. -/// Check the [faq] for details. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html -/// [faq]: https://man7.org/linux/man-pages/man7/epoll.7.html#:~:text=Will%20closing%20a%20file%20descriptor%20cause%20it%20to%20be%20removed%20from%20all%0A%20%20%20%20%20%20%20%20%20%20epoll%20interest%20lists%3F -#[doc(alias = "epoll_ctl")] -#[inline] -pub fn add( - epoll: impl AsFd, - source: impl AsFd, - data: EventData, - event_flags: EventFlags, -) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. - unsafe { - syscalls::epoll_add( - epoll.as_fd(), - source.as_fd().as_raw_fd(), - &Event { - flags: event_flags, - data, - }, - ) - } -} - -/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in a -/// given epoll object. -/// -/// This sets the events of interest with `target` to `events`. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html -#[doc(alias = "epoll_ctl")] -#[inline] -pub fn modify( - epoll: impl AsFd, - source: impl AsFd, - data: EventData, - event_flags: EventFlags, -) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. - unsafe { - let raw_fd = source.as_fd().as_raw_fd(); - syscalls::epoll_mod( - epoll.as_fd(), - raw_fd, - &Event { - flags: event_flags, - data, - }, - ) - } -} - -/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a -/// given epoll object. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html -#[doc(alias = "epoll_ctl")] -#[inline] -pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. - unsafe { - let raw_fd = source.as_fd().as_raw_fd(); - syscalls::epoll_del(epoll.as_fd(), raw_fd) - } -} - -/// `epoll_wait(self, events, timeout)`—Waits for registered events of -/// interest. -/// -/// For each event of interest, an element is written to `events`. On -/// success, this returns the number of written elements. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html -#[cfg(feature = "alloc")] -#[cfg_attr(docsrs, doc(cfg(feature = "alloc"), alias = "epoll_wait"))] -#[inline] -pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { - // SAFETY: We're calling `epoll_wait` via FFI and we know how it - // behaves. - unsafe { - event_list.events.set_len(0); - let nfds = syscalls::epoll_wait( - epoll.as_fd(), - event_list.events[..].as_mut_ptr().cast(), - event_list.events.capacity(), - timeout, - )?; - event_list.events.set_len(nfds); - } - - Ok(()) -} - -/// An iterator over the `Event`s in an `EventVec`. -pub struct Iter<'a> { - /// Use `Copied` to copy the struct, since `Event` is `packed` on some - /// platforms, and it's common for users to directly destructure it, which - /// would lead to errors about forming references to packed fields. - iter: core::iter::Copied>, -} - -impl<'a> Iterator for Iter<'a> { - type Item = Event; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -/// A record of an event that occurred. -#[repr(C)] -#[cfg_attr(target_arch = "x86_64", repr(packed))] -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct Event { - /// Which specific event(s) occurred. - pub flags: EventFlags, - /// User data. - pub data: EventData, -} - -/// Data associated with an [`Event`]. This can either be a 64-bit integer -/// value or a pointer which preserves pointer provenance. -#[repr(C)] -#[derive(Copy, Clone)] -pub union EventData { - /// A 64-bit integer value. - as_u64: u64, - - /// A `*mut c_void` which preserves pointer provenance, extended to be - /// 64-bit so that if we read the value as a `u64` union field, we don't - /// get uninitialized memory. - sixty_four_bit_pointer: SixtyFourBitPointer, -} - -impl EventData { - /// Construct a new value containing a `u64`. - #[inline] - pub const fn new_u64(value: u64) -> Self { - Self { as_u64: value } - } - - /// Construct a new value containing a `*mut c_void`. - #[inline] - pub const fn new_ptr(value: *mut c_void) -> Self { - Self { - sixty_four_bit_pointer: SixtyFourBitPointer { - pointer: value, - #[cfg(target_pointer_width = "32")] - _padding: 0, - }, - } - } - - /// Return the value as a `u64`. - /// - /// If the stored value was a pointer, the pointer is zero-extended to a - /// `u64`. - #[inline] - pub fn u64(self) -> u64 { - unsafe { self.as_u64 } - } - - /// Return the value as a `*mut c_void`. - /// - /// If the stored value was a `u64`, the least-significant bits of the - /// `u64` are returned as a pointer value. - #[inline] - pub fn ptr(self) -> *mut c_void { - unsafe { self.sixty_four_bit_pointer.pointer } - } -} - -impl PartialEq for EventData { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.u64() == other.u64() - } -} - -impl Eq for EventData {} - -impl Hash for EventData { - #[inline] - fn hash(&self, state: &mut H) { - self.u64().hash(state) - } -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct SixtyFourBitPointer { - #[cfg(target_endian = "big")] - #[cfg(target_pointer_width = "32")] - _padding: u32, - - pointer: *mut c_void, - - #[cfg(target_endian = "little")] - #[cfg(target_pointer_width = "32")] - _padding: u32, -} - -/// A vector of `Event`s, plus context for interpreting them. -#[cfg(feature = "alloc")] -pub struct EventVec { - events: Vec, -} - -#[cfg(feature = "alloc")] -impl EventVec { - /// Constructs an `EventVec` from raw pointer, length, and capacity. - /// - /// # Safety - /// - /// This function calls [`Vec::from_raw_parts`] with its arguments. - /// - /// [`Vec::from_raw_parts`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts - #[inline] - pub unsafe fn from_raw_parts(ptr: *mut Event, len: usize, capacity: usize) -> Self { - Self { - events: Vec::from_raw_parts(ptr, len, capacity), - } - } - - /// Constructs an `EventVec` with memory for `capacity` `Event`s. - #[inline] - pub fn with_capacity(capacity: usize) -> Self { - Self { - events: Vec::with_capacity(capacity), - } - } - - /// Returns the current `Event` capacity of this `EventVec`. - #[inline] - pub fn capacity(&self) -> usize { - self.events.capacity() - } - - /// Reserves enough memory for at least `additional` more `Event`s. - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.events.reserve(additional); - } - - /// Reserves enough memory for exactly `additional` more `Event`s. - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.events.reserve_exact(additional); - } - - /// Clears all the `Events` out of this `EventVec`. - #[inline] - pub fn clear(&mut self) { - self.events.clear(); - } - - /// Shrinks the capacity of this `EventVec` as much as possible. - #[inline] - pub fn shrink_to_fit(&mut self) { - self.events.shrink_to_fit(); - } - - /// Returns an iterator over the `Event`s in this `EventVec`. - #[inline] - pub fn iter(&self) -> Iter<'_> { - Iter { - iter: self.events.iter().copied(), - } - } - - /// Returns the number of `Event`s logically contained in this `EventVec`. - #[inline] - pub fn len(&mut self) -> usize { - self.events.len() - } - - /// Tests whether this `EventVec` is logically empty. - #[inline] - pub fn is_empty(&mut self) -> bool { - self.events.is_empty() - } -} - -#[cfg(feature = "alloc")] -impl<'a> IntoIterator for &'a EventVec { - type IntoIter = Iter<'a>; - type Item = Event; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -#[test] -fn test_epoll_layouts() { - check_renamed_type!(Event, epoll_event); - check_renamed_type!(Event, epoll_event); - check_renamed_struct_renamed_field!(Event, epoll_event, flags, events); - check_renamed_struct_renamed_field!(Event, epoll_event, data, data); -} diff --git a/src/backend/linux_raw/event/syscalls.rs b/src/backend/linux_raw/event/syscalls.rs index 323e09d59..abae5c62e 100644 --- a/src/backend/linux_raw/event/syscalls.rs +++ b/src/backend/linux_raw/event/syscalls.rs @@ -6,19 +6,13 @@ #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] use crate::backend::c; -#[cfg(any( - feature = "alloc", - not(any(target_arch = "aarch64", target_arch = "riscv64")) -))] -use crate::backend::conv::c_int; -#[cfg(feature = "alloc")] -use crate::backend::conv::pass_usize; use crate::backend::conv::{ - by_ref, c_uint, raw_fd, ret, ret_error, ret_owned_fd, ret_usize, slice_mut, zero, + by_ref, c_int, c_uint, ret, ret_error, ret_owned_fd, ret_usize, slice_mut, zero, }; use crate::event::{epoll, EventfdFlags, PollFd}; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; +use core::mem::MaybeUninit; use linux_raw_sys::general::{EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD}; #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use { @@ -61,61 +55,66 @@ pub(crate) fn epoll_create(flags: epoll::CreateFlags) -> io::Result { } #[inline] -pub(crate) unsafe fn epoll_add( +pub(crate) fn epoll_add( epfd: BorrowedFd<'_>, - fd: c::c_int, + fd: BorrowedFd, event: &epoll::Event, ) -> io::Result<()> { - ret(syscall_readonly!( - __NR_epoll_ctl, - epfd, - c_uint(EPOLL_CTL_ADD), - raw_fd(fd), - by_ref(event) - )) + unsafe { + ret(syscall_readonly!( + __NR_epoll_ctl, + epfd, + c_uint(EPOLL_CTL_ADD), + fd, + by_ref(event) + )) + } } #[inline] -pub(crate) unsafe fn epoll_mod( +pub(crate) fn epoll_mod( epfd: BorrowedFd<'_>, - fd: c::c_int, + fd: BorrowedFd, event: &epoll::Event, ) -> io::Result<()> { - ret(syscall_readonly!( - __NR_epoll_ctl, - epfd, - c_uint(EPOLL_CTL_MOD), - raw_fd(fd), - by_ref(event) - )) + unsafe { + ret(syscall_readonly!( + __NR_epoll_ctl, + epfd, + c_uint(EPOLL_CTL_MOD), + fd, + by_ref(event) + )) + } } #[inline] -pub(crate) unsafe fn epoll_del(epfd: BorrowedFd<'_>, fd: c::c_int) -> io::Result<()> { - ret(syscall_readonly!( - __NR_epoll_ctl, - epfd, - c_uint(EPOLL_CTL_DEL), - raw_fd(fd), - zero() - )) +pub(crate) fn epoll_del(epfd: BorrowedFd<'_>, fd: BorrowedFd) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_epoll_ctl, + epfd, + c_uint(EPOLL_CTL_DEL), + fd, + zero() + )) + } } -#[cfg(feature = "alloc")] #[inline] pub(crate) fn epoll_wait( epfd: BorrowedFd<'_>, - events: *mut epoll::Event, - num_events: usize, + events: &mut [MaybeUninit], timeout: c::c_int, ) -> io::Result { + let (buf_addr_mut, buf_len) = slice_mut(events); #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] unsafe { ret_usize(syscall!( __NR_epoll_wait, epfd, - events, - pass_usize(num_events), + buf_addr_mut, + buf_len, c_int(timeout) )) } @@ -124,8 +123,8 @@ pub(crate) fn epoll_wait( ret_usize(syscall!( __NR_epoll_pwait, epfd, - events, - pass_usize(num_events), + buf_addr_mut, + buf_len, c_int(timeout), zero() )) diff --git a/src/event/epoll.rs b/src/event/epoll.rs new file mode 100644 index 000000000..1afd74910 --- /dev/null +++ b/src/event/epoll.rs @@ -0,0 +1,444 @@ +//! Linux `epoll` support. +//! +//! # Examples +//! +//! ```no_run +//! # #[cfg(feature = "net")] +//! # fn main() -> std::io::Result<()> { +//! use rustix::event::epoll; +//! use rustix::fd::AsFd; +//! use rustix::io::{ioctl_fionbio, read, write}; +//! use rustix::net::{ +//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, SocketAddrV4, SocketType, +//! }; +//! use std::collections::HashMap; +//! use std::os::unix::io::AsRawFd; +//! +//! // Create a socket and listen on it. +//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, None)?; +//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?; +//! listen(&listen_sock, 1)?; +//! +//! // Create an epoll object. Using `Owning` here means the epoll object will +//! // take ownership of the file descriptors registered with it. +//! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?; +//! +//! // Register the socket with the epoll object. +//! epoll::add( +//! &epoll, +//! &listen_sock, +//! epoll::EventData::new_u64(1), +//! epoll::EventFlags::IN, +//! )?; +//! +//! // Keep track of the sockets we've opened. +//! let mut next_id = epoll::EventData::new_u64(2); +//! let mut sockets = HashMap::new(); +//! +//! // Process events. +//! let mut event_list = epoll::EventVec::with_capacity(4); +//! loop { +//! epoll::wait(&epoll, &mut event_list, -1)?; +//! for event in &event_list { +//! let target = event.data; +//! if target.u64() == 1 { +//! // Accept a new connection, set it to non-blocking, and +//! // register to be notified when it's ready to write to. +//! let conn_sock = accept(&listen_sock)?; +//! ioctl_fionbio(&conn_sock, true)?; +//! epoll::add( +//! &epoll, +//! &conn_sock, +//! next_id, +//! epoll::EventFlags::OUT | epoll::EventFlags::ET, +//! )?; +//! +//! // Keep track of the socket. +//! sockets.insert(next_id, conn_sock); +//! next_id = epoll::EventData::new_u64(next_id.u64() + 1); +//! } else { +//! // Write a message to the stream and then unregister it. +//! let target = sockets.remove(&target).unwrap(); +//! write(&target, b"hello\n")?; +//! let _ = epoll::delete(&epoll, &target)?; +//! } +//! } +//! } +//! # } +//! # #[cfg(not(feature = "net"))] +//! # fn main() {} +//! ``` + +#![allow(unsafe_code)] +#![allow(unused_qualifications)] + +use super::epoll; +use crate::backend::c; +pub use crate::backend::event::epoll::*; +use crate::backend::event::syscalls; +use crate::fd::{AsFd, OwnedFd}; +use crate::io; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +use core::ffi::c_void; +use core::hash::{Hash, Hasher}; +use core::slice; + +/// `epoll_create1(flags)`—Creates a new epoll object. +/// +/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file +/// descriptor from being implicitly passed across `exec` boundaries. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_create.2.html +#[inline] +#[doc(alias = "epoll_create1")] +pub fn create(flags: epoll::CreateFlags) -> io::Result { + syscalls::epoll_create(flags) +} + +/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll +/// object. +/// +/// This registers interest in any of the events set in `event_flags` occurring on +/// the file descriptor associated with `data`. +/// +/// Note that `close`ing a file descriptor does not necessarily unregister +/// interest which can lead to spurious events being returned from [`epoll::wait`]. If +/// a file descriptor is an `Arc`, then `epoll` can be +/// thought to maintain a `Weak` to the file descriptor. +/// Check the [faq] for details. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +/// [faq]: https://man7.org/linux/man-pages/man7/epoll.7.html#:~:text=Will%20closing%20a%20file%20descriptor%20cause%20it%20to%20be%20removed%20from%20all%0A%20%20%20%20%20%20%20%20%20%20epoll%20interest%20lists%3F +#[doc(alias = "epoll_ctl")] +#[inline] +pub fn add( + epoll: impl AsFd, + source: impl AsFd, + data: epoll::EventData, + event_flags: epoll::EventFlags, +) -> io::Result<()> { + syscalls::epoll_add( + epoll.as_fd(), + source.as_fd(), + &mut Event { + flags: event_flags, + data, + #[cfg(all(libc, target_os = "redox"))] + _pad: 0, + }, + ) +} + +/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in a +/// given epoll object. +/// +/// This sets the events of interest with `target` to `events`. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +#[doc(alias = "epoll_ctl")] +#[inline] +pub fn modify( + epoll: impl AsFd, + source: impl AsFd, + data: epoll::EventData, + event_flags: epoll::EventFlags, +) -> io::Result<()> { + syscalls::epoll_mod( + epoll.as_fd(), + source.as_fd(), + &mut Event { + flags: event_flags, + data, + #[cfg(all(libc, target_os = "redox"))] + _pad: 0, + }, + ) +} + +/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a +/// given epoll object. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +#[doc(alias = "epoll_ctl")] +#[inline] +pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { + syscalls::epoll_del(epoll.as_fd(), source.as_fd()) +} + +/// `epoll_wait(self, events, timeout)`—Waits for registered events of +/// interest. +/// +/// For each event of interest, an element is written to `events`. On +/// success, this returns the number of written elements. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc"), alias = "epoll_wait"))] +#[inline] +pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { + // SAFETY: We're calling `epoll_wait` via FFI and we know how it + // behaves. + unsafe { + event_list.events.clear(); + let nfds = syscalls::epoll_wait( + epoll.as_fd(), + event_list.events.spare_capacity_mut(), + timeout, + )?; + event_list.events.set_len(nfds); + } + + Ok(()) +} + +/// An iterator over the `Event`s in an `EventVec`. +pub struct Iter<'a> { + /// Use `Copied` to copy the struct, since `Event` is `packed` on some + /// platforms, and it's common for users to directly destructure it, which + /// would lead to errors about forming references to packed fields. + iter: core::iter::Copied>, +} + +impl<'a> Iterator for Iter<'a> { + type Item = Event; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +/// A record of an event that occurred. +#[repr(C)] +#[cfg_attr(all(not(libc), target_arch = "x86_64"), repr(packed))] +#[cfg_attr( + all( + libc, + linux_kernel, + any( + all( + target_arch = "x86", + not(target_env = "musl"), + not(target_os = "android"), + ), + target_arch = "x86_64", + ) + ), + repr(packed) +)] +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct Event { + /// Which specific event(s) occurred. + pub flags: EventFlags, + /// User data. + pub data: EventData, + + #[cfg(all(libc, target_os = "redox"))] + _pad: u64, +} + +/// Data associated with an [`Event`]. This can either be a 64-bit integer +/// value or a pointer which preserves pointer provenance. +#[repr(C)] +#[derive(Copy, Clone)] +pub union EventData { + /// A 64-bit integer value. + as_u64: u64, + + /// A `*mut c_void` which preserves pointer provenance, extended to be + /// 64-bit so that if we read the value as a `u64` union field, we don't + /// get uninitialized memory. + sixty_four_bit_pointer: SixtyFourBitPointer, +} + +impl EventData { + /// Construct a new value containing a `u64`. + #[inline] + pub const fn new_u64(value: u64) -> Self { + Self { as_u64: value } + } + + /// Construct a new value containing a `*mut c_void`. + #[inline] + pub const fn new_ptr(value: *mut c_void) -> Self { + Self { + sixty_four_bit_pointer: SixtyFourBitPointer { + pointer: value, + #[cfg(target_pointer_width = "32")] + _padding: 0, + }, + } + } + + /// Return the value as a `u64`. + /// + /// If the stored value was a pointer, the pointer is zero-extended to a + /// `u64`. + #[inline] + pub fn u64(self) -> u64 { + unsafe { self.as_u64 } + } + + /// Return the value as a `*mut c_void`. + /// + /// If the stored value was a `u64`, the least-significant bits of the + /// `u64` are returned as a pointer value. + #[inline] + pub fn ptr(self) -> *mut c_void { + unsafe { self.sixty_four_bit_pointer.pointer } + } +} + +impl PartialEq for EventData { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.u64() == other.u64() + } +} + +impl Eq for EventData {} + +impl Hash for EventData { + #[inline] + fn hash(&self, state: &mut H) { + self.u64().hash(state) + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct SixtyFourBitPointer { + #[cfg(target_endian = "big")] + #[cfg(target_pointer_width = "32")] + _padding: u32, + + pointer: *mut c_void, + + #[cfg(target_endian = "little")] + #[cfg(target_pointer_width = "32")] + _padding: u32, +} + +/// A vector of `Event`s, plus context for interpreting them. +#[cfg(feature = "alloc")] +pub struct EventVec { + events: Vec, +} + +#[cfg(feature = "alloc")] +impl EventVec { + /// Constructs an `EventVec` from raw pointer, length, and capacity. + /// + /// # Safety + /// + /// This function calls [`Vec::from_raw_parts`] with its arguments. + /// + /// [`Vec::from_raw_parts`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts + #[inline] + pub unsafe fn from_raw_parts(ptr: *mut Event, len: usize, capacity: usize) -> Self { + Self { + events: Vec::from_raw_parts(ptr, len, capacity), + } + } + + /// Constructs an `EventVec` with memory for `capacity` `Event`s. + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self { + events: Vec::with_capacity(capacity), + } + } + + /// Returns the current `Event` capacity of this `EventVec`. + #[inline] + pub fn capacity(&self) -> usize { + self.events.capacity() + } + + /// Reserves enough memory for at least `additional` more `Event`s. + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.events.reserve(additional); + } + + /// Reserves enough memory for exactly `additional` more `Event`s. + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.events.reserve_exact(additional); + } + + /// Clears all the `Events` out of this `EventVec`. + #[inline] + pub fn clear(&mut self) { + self.events.clear(); + } + + /// Shrinks the capacity of this `EventVec` as much as possible. + #[inline] + pub fn shrink_to_fit(&mut self) { + self.events.shrink_to_fit(); + } + + /// Returns an iterator over the `Event`s in this `EventVec`. + #[inline] + pub fn iter(&self) -> Iter<'_> { + Iter { + iter: self.events.iter().copied(), + } + } + + /// Returns the number of `Event`s logically contained in this `EventVec`. + #[inline] + pub fn len(&mut self) -> usize { + self.events.len() + } + + /// Tests whether this `EventVec` is logically empty. + #[inline] + pub fn is_empty(&mut self) -> bool { + self.events.is_empty() + } +} + +#[cfg(feature = "alloc")] +impl<'a> IntoIterator for &'a EventVec { + type IntoIter = Iter<'a>; + type Item = Event; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +#[test] +fn test_epoll_layouts() { + check_renamed_type!(Event, epoll_event); + check_renamed_type!(Event, epoll_event); + check_renamed_struct_renamed_field!(Event, epoll_event, flags, events); + #[cfg(libc)] + check_renamed_struct_renamed_field!(Event, epoll_event, data, u64); + #[cfg(not(libc))] + check_renamed_struct_renamed_field!(Event, epoll_event, data, data); +} diff --git a/src/event/mod.rs b/src/event/mod.rs index c497febe0..dab9c6932 100644 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -1,5 +1,7 @@ //! Event operations. +#[cfg(any(linux_kernel, target_os = "redox"))] +pub mod epoll; #[cfg(any( linux_kernel, target_os = "freebsd", @@ -15,8 +17,6 @@ mod poll; #[cfg(solarish)] pub mod port; -#[cfg(any(linux_kernel, target_os = "redox"))] -pub use crate::backend::event::epoll; #[cfg(any( linux_kernel, target_os = "freebsd",