From 9ea1a84c9ec21cb7fedc34f604d12b909f2cf222 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 16 Jun 2023 09:52:59 -0700 Subject: [PATCH] Improve strict-provenance compatibility in the `epoll` API. Change the user-data field of epoll's `Event` from a bare `u64` to a `union` which can be either a `u64` or a `*mut c_void` to allowe users to store pointers in it that preserve strict provenance. Also, rename `epoll::epoll_add` and similar to just `epoll::add` and similar, for tidiness. --- src/backend/libc/event/epoll.rs | 185 ++++++++++++++++++------ src/backend/linux_raw/event/epoll.rs | 161 ++++++++++++++++----- src/backend/linux_raw/event/syscalls.rs | 8 +- tests/event/epoll.rs | 45 +++++- 4 files changed, 308 insertions(+), 91 deletions(-) diff --git a/src/backend/libc/event/epoll.rs b/src/backend/libc/event/epoll.rs index e09d1ecdb..6f64ab6dc 100644 --- a/src/backend/libc/event/epoll.rs +++ b/src/backend/libc/event/epoll.rs @@ -26,27 +26,32 @@ //! //! // Create an epoll object. Using `Owning` here means the epoll object will //! // take ownership of the file descriptors registered with it. -//! let epoll = epoll::epoll_create(epoll::CreateFlags::CLOEXEC)?; +//! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?; //! //! // Register the socket with the epoll object. -//! epoll::epoll_add(&epoll, &listen_sock, 1, epoll::EventFlags::IN)?; +//! 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 = 2; +//! 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::epoll_wait(&epoll, &mut event_list, -1)?; +//! epoll::wait(&epoll, &mut event_list, -1)?; //! for event in &event_list { //! let target = event.data; -//! if target == 1 { +//! 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::epoll_add( +//! epoll::add( //! &epoll, //! &conn_sock, //! next_id, @@ -55,12 +60,12 @@ //! //! // Keep track of the socket. //! sockets.insert(next_id, conn_sock); -//! next_id += 1; +//! 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::epoll_del(&epoll, &target)?; +//! let _ = epoll::delete(&epoll, &target)?; //! } //! } //! } @@ -73,12 +78,16 @@ use crate::backend::c; use crate::backend::conv::{ret, ret_owned_fd, ret_u32}; use crate::fd::{AsFd, AsRawFd, OwnedFd}; use crate::io; +use crate::utils::as_mut_ptr; 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 [`Epoll::new`]. + /// `EPOLL_*` for use with [`new`]. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: c::c_int { /// `EPOLL_CLOEXEC` @@ -87,7 +96,7 @@ bitflags! { } bitflags! { - /// `EPOLL*` for use with [`Epoll::add`]. + /// `EPOLL*` for use with [`add`]. #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct EventFlags: u32 { /// `EPOLLIN` @@ -138,82 +147,91 @@ bitflags! { } } -/// `epoll_create1(flags)`—Creates a new `Epoll`. +/// `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. #[inline] #[doc(alias = "epoll_create1")] -pub fn epoll_create(flags: CreateFlags) -> io::Result { +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(flags.bits())) } } /// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an -/// `Epoll`. +/// epoll object. /// -/// If `epoll_del` is not called on the I/O source passed into this function +/// This registers interest in any of the events set in `events` occurring +/// on the file descriptor associated with `data`. +/// +/// If [`delete`] is not called on the I/O source passed into this function /// before the I/O source is `close`d, then the `epoll` will act as if the I/O /// source is still registered with it. This can lead to spurious events being -/// returned from `epoll_wait`. If a file descriptor is an +/// returned from [`wait`]. If a file descriptor is an /// `Arc`, then `epoll` can be thought to maintain a /// `Weak` to the file descriptor. #[doc(alias = "epoll_ctl")] -pub fn epoll_add( +pub fn add( epoll: impl AsFd, source: impl AsFd, - data: u64, + data: EventData, event_flags: EventFlags, ) -> io::Result<()> { // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. + // 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 elsehwere 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, - &mut c::epoll_event { - events: event_flags.bits(), - r#u64: data, - }, + as_mut_ptr(&mut Event { + flags: event_flags, + data, + }) + .cast(), )) } } /// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in -/// this `Epoll`. +/// a given epoll object. /// /// This sets the events of interest with `target` to `events`. #[doc(alias = "epoll_ctl")] -pub fn epoll_mod( +pub fn modify( epoll: impl AsFd, source: impl AsFd, - data: u64, + 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. + // 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 elsehwere for layout equivalence. unsafe { ret(c::epoll_ctl( epoll.as_fd().as_raw_fd(), c::EPOLL_CTL_MOD, raw_fd, - &mut c::epoll_event { - events: event_flags.bits(), - r#u64: data, - }, + as_mut_ptr(&mut Event { + flags: event_flags, + data, + }) + .cast(), )) } } /// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in -/// this `Epoll`. +/// a given epoll object. #[doc(alias = "epoll_ctl")] -pub fn epoll_del(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { +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 { @@ -232,11 +250,7 @@ pub fn epoll_del(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { /// /// For each event of interest, an element is written to `events`. On /// success, this returns the number of written elements. -pub fn epoll_wait( - epoll: impl AsFd, - event_list: &mut EventVec, - timeout: c::c_int, -) -> io::Result<()> { +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 { @@ -255,12 +269,13 @@ pub fn epoll_wait( /// An iterator over the `Event`s in an `EventVec`. pub struct Iter<'a> { - iter: core::slice::Iter<'a, Event>, + iter: slice::Iter<'a, Event>, } impl<'a> Iterator for Iter<'a> { type Item = &'a Event; + #[inline] fn next(&mut self) -> Option { self.iter.next() } @@ -281,11 +296,91 @@ impl<'a> Iterator for Iter<'a> { )] pub struct Event { /// Which specific event(s) occurred. - // Match the layout of `c::epoll_event`. We just use a `u64` instead of - // the full union. - pub event_flags: EventFlags, + pub flags: EventFlags, /// User data. - pub data: u64, + pub data: EventData, +} + +/// Data assocated 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 { + /// 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 } + } + + /// Construct a new value containing a `u64`. + #[inline] + pub fn new_u64(value: u64) -> Self { + Self { as_u64: value } + } + + /// Construct a new value containing a `*mut c_void`. + #[inline] + pub fn new_ptr(value: *mut c_void) -> Self { + Self { + sixty_four_bit_pointer: SixtyFourBitPointer { + pointer: value, + #[cfg(target_pointer_width = "32")] + _padding: 0, + }, + } + } +} + +impl PartialEq for EventData { + #[inline] + fn eq(&self, other: &EventData) -> 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. @@ -376,3 +471,9 @@ impl<'a> IntoIterator for &'a EventVec { self.iter() } } + +#[test] +fn test_epoll_layouts() { + // FIXME: once the termios patch lands, use the new type checking + // macros to check that `Event`'s layout matches `epoll_event`. +} diff --git a/src/backend/linux_raw/event/epoll.rs b/src/backend/linux_raw/event/epoll.rs index e810f156c..5845b80c9 100644 --- a/src/backend/linux_raw/event/epoll.rs +++ b/src/backend/linux_raw/event/epoll.rs @@ -26,27 +26,32 @@ //! //! // Create an epoll object. Using `Owning` here means the epoll object will //! // take ownership of the file descriptors registered with it. -//! let epoll = epoll::epoll_create(epoll::CreateFlags::CLOEXEC)?; +//! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?; //! //! // Register the socket with the epoll object. -//! epoll::epoll_add(&epoll, &listen_sock, 1, epoll::EventFlags::IN)?; +//! 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 = 2; +//! 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::epoll_wait(&epoll, &mut event_list, -1)?; +//! epoll::wait(&epoll, &mut event_list, -1)?; //! for event in &event_list { //! let target = event.data; -//! if target == 1 { +//! 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::epoll_add( +//! epoll::add( //! &epoll, //! &conn_sock, //! next_id, @@ -55,12 +60,12 @@ //! //! // Keep track of the socket. //! sockets.insert(next_id, conn_sock); -//! next_id += 1; +//! 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::epoll_del(&epoll, &target)?; +//! let _ = epoll::delete(&epoll, &target)?; //! } //! } //! } @@ -77,10 +82,12 @@ use crate::fd::{AsFd, AsRawFd, OwnedFd}; use crate::io; 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 [`Epoll::new`]. + /// `EPOLL_*` for use with [`new`]. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: c::c_uint { /// `EPOLL_CLOEXEC` @@ -89,7 +96,7 @@ bitflags! { } bitflags! { - /// `EPOLL*` for use with [`Epoll::add`]. + /// `EPOLL*` for use with [`add`]. #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct EventFlags: u32 { /// `EPOLLIN` @@ -139,34 +146,34 @@ bitflags! { } } -/// `epoll_create1(flags)`—Creates a new `Epoll`. +/// `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. #[inline] #[doc(alias = "epoll_create1")] -pub fn epoll_create(flags: CreateFlags) -> io::Result { +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`. +/// epoll object. /// /// This registers interest in any of the events set in `events` occurring /// on the file descriptor associated with `data`. /// -/// If `epoll_del` is not called on the I/O source passed into this function +/// If [`delete`] is not called on the I/O source passed into this function /// before the I/O source is `close`d, then the `epoll` will act as if the I/O /// source is still registered with it. This can lead to spurious events being -/// returned from `epoll_wait`. If a file descriptor is an +/// returned from [`wait`]. If a file descriptor is an /// `Arc`, then `epoll` can be thought to maintain a /// `Weak` to the file descriptor. #[doc(alias = "epoll_ctl")] #[inline] -pub fn epoll_add( +pub fn add( epoll: impl AsFd, source: impl AsFd, - data: u64, + data: EventData, event_flags: EventFlags, ) -> io::Result<()> { // SAFETY: We're calling `epoll_ctl` via FFI and we know how it @@ -175,8 +182,8 @@ pub fn epoll_add( syscalls::epoll_add( epoll.as_fd(), source.as_fd().as_raw_fd(), - &linux_raw_sys::general::epoll_event { - events: event_flags.bits(), + &Event { + flags: event_flags, data, }, ) @@ -184,15 +191,15 @@ pub fn epoll_add( } /// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in -/// this `Epoll`. +/// a given epoll object. /// /// This sets the events of interest with `target` to `events`. #[doc(alias = "epoll_ctl")] #[inline] -pub fn epoll_mod( +pub fn modify( epoll: impl AsFd, source: impl AsFd, - data: u64, + data: EventData, event_flags: EventFlags, ) -> io::Result<()> { // SAFETY: We're calling `epoll_ctl` via FFI and we know how it @@ -202,8 +209,8 @@ pub fn epoll_mod( syscalls::epoll_mod( epoll.as_fd(), raw_fd, - &linux_raw_sys::general::epoll_event { - events: event_flags.bits(), + &Event { + flags: event_flags, data, }, ) @@ -211,12 +218,10 @@ pub fn epoll_mod( } /// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in -/// this `Epoll`. -/// -/// This also returns the owning `Data`. +/// a given epoll object. #[doc(alias = "epoll_ctl")] #[inline] -pub fn epoll_del(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { +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 { @@ -231,11 +236,7 @@ pub fn epoll_del(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { /// For each event of interest, an element is written to `events`. On /// success, this returns the number of written elements. #[inline] -pub fn epoll_wait( - epoll: impl AsFd, - event_list: &mut EventVec, - timeout: c::c_int, -) -> io::Result<()> { +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 { @@ -271,11 +272,91 @@ impl<'a> Iterator for Iter<'a> { #[cfg_attr(target_arch = "x86_64", repr(packed))] pub struct Event { /// Which specific event(s) occurred. - // Match the layout of `linux_raw_sys::general::epoll_event`. We just use a - // `u64` instead of the full union. - pub event_flags: EventFlags, + pub flags: EventFlags, /// User data. - pub data: u64, + pub data: EventData, +} + +/// Data assocated 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 { + /// 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 } + } + + /// Construct a new value containing a `u64`. + #[inline] + pub fn new_u64(value: u64) -> Self { + Self { as_u64: value } + } + + /// Construct a new value containing a `*mut c_void`. + #[inline] + pub fn new_ptr(value: *mut c_void) -> Self { + Self { + sixty_four_bit_pointer: SixtyFourBitPointer { + pointer: value, + #[cfg(target_pointer_width = "32")] + _padding: 0, + }, + } + } +} + +impl PartialEq for EventData { + #[inline] + fn eq(&self, other: &EventData) -> 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. @@ -366,3 +447,9 @@ impl<'a> IntoIterator for &'a EventVec { self.iter() } } + +#[test] +fn test_epoll_layouts() { + // FIXME: once the termios patch lands, use the new type checking + // macros to check that `Event`'s layout matches `epoll_event`. +} diff --git a/src/backend/linux_raw/event/syscalls.rs b/src/backend/linux_raw/event/syscalls.rs index f504cc1f7..6d6b35d75 100644 --- a/src/backend/linux_raw/event/syscalls.rs +++ b/src/backend/linux_raw/event/syscalls.rs @@ -13,7 +13,7 @@ use crate::backend::conv::{ use crate::event::{epoll, EventfdFlags, PollFd}; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; -use linux_raw_sys::general::{epoll_event, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD}; +use linux_raw_sys::general::{EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD}; #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use { crate::backend::conv::{opt_ref, size_of}, @@ -58,7 +58,7 @@ pub(crate) fn epoll_create(flags: epoll::CreateFlags) -> io::Result { pub(crate) unsafe fn epoll_add( epfd: BorrowedFd<'_>, fd: c::c_int, - event: &epoll_event, + event: &epoll::Event, ) -> io::Result<()> { ret(syscall_readonly!( __NR_epoll_ctl, @@ -73,7 +73,7 @@ pub(crate) unsafe fn epoll_add( pub(crate) unsafe fn epoll_mod( epfd: BorrowedFd<'_>, fd: c::c_int, - event: &epoll_event, + event: &epoll::Event, ) -> io::Result<()> { ret(syscall_readonly!( __NR_epoll_ctl, @@ -98,7 +98,7 @@ pub(crate) unsafe fn epoll_del(epfd: BorrowedFd<'_>, fd: c::c_int) -> io::Result #[inline] pub(crate) fn epoll_wait( epfd: BorrowedFd<'_>, - events: *mut epoll_event, + events: *mut epoll::Event, num_events: usize, timeout: c::c_int, ) -> io::Result { diff --git a/tests/event/epoll.rs b/tests/event/epoll.rs index 5ca0fd246..b55d71f09 100644 --- a/tests/event/epoll.rs +++ b/tests/event/epoll.rs @@ -5,6 +5,7 @@ use rustix::net::{ SocketAddrAny, SocketAddrV4, SocketType, }; use std::collections::HashMap; +use std::ffi::c_void; use std::sync::{Arc, Condvar, Mutex}; use std::thread; @@ -27,22 +28,28 @@ fn server(ready: Arc<(Mutex, Condvar)>) { cvar.notify_all(); } - let epoll = epoll::epoll_create(epoll::CreateFlags::CLOEXEC).unwrap(); + let epoll = epoll::create(epoll::CreateFlags::CLOEXEC).unwrap(); - epoll::epoll_add(&epoll, &listen_sock, 1, epoll::EventFlags::IN).unwrap(); + epoll::add( + &epoll, + &listen_sock, + epoll::EventData::new_u64(1), + epoll::EventFlags::IN, + ) + .unwrap(); - let mut next_data = 2; + let mut next_data = epoll::EventData::new_u64(2); let mut targets = HashMap::new(); let mut event_list = epoll::EventVec::with_capacity(4); loop { - epoll::epoll_wait(&epoll, &mut event_list, -1).unwrap(); + epoll::wait(&epoll, &mut event_list, -1).unwrap(); for event in &event_list { let target = event.data; - if target == 1 { + if target.u64() == 1 { let conn_sock = accept(&listen_sock).unwrap(); ioctl_fionbio(&conn_sock, true).unwrap(); - epoll::epoll_add( + epoll::add( &epoll, &conn_sock, next_data, @@ -50,11 +57,11 @@ fn server(ready: Arc<(Mutex, Condvar)>) { ) .unwrap(); targets.insert(next_data, conn_sock); - next_data += 1; + next_data = epoll::EventData::new_u64(next_data.u64() + 1); } else { let target = targets.remove(&target).unwrap(); write(&target, b"hello\n").unwrap(); - epoll::epoll_del(&epoll, &target).unwrap(); + epoll::delete(&epoll, &target).unwrap(); } } } @@ -102,3 +109,25 @@ fn test_epoll() { .unwrap(); client.join().unwrap(); } + +#[test] +fn test_epoll_event_data() { + let d = epoll::EventData::new_u64(0); + assert_eq!(d.u64(), 0); + assert_eq!(d.ptr() as u64, 0); + let d = epoll::EventData::new_u64(1); + assert_eq!(d.u64(), 1); + assert_eq!(d.ptr() as u64, 1); + let d = epoll::EventData::new_u64(!5); + assert_eq!(d.u64(), !5 as *mut c_void as u64); + assert_eq!(d.ptr() as u64, !5 as *mut c_void as u64); + let d = epoll::EventData::new_ptr(core::ptr::null_mut()); + assert_eq!(d.u64(), 0); + assert!(d.ptr().is_null()); + let d = epoll::EventData::new_ptr(3 as *mut c_void); + assert_eq!(d.u64(), 3); + assert_eq!(d.ptr() as u64, 3); + let d = epoll::EventData::new_ptr(!3 as *mut c_void); + assert_eq!(d.u64(), !3 as *mut c_void as u64); + assert_eq!(d.ptr() as u64, !3 as *mut c_void as u64); +}