//! 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() {} //! ```
usecrate::backend::c; #[cfg(feature = "alloc")] usecrate::backend::conv::ret_u32; usecrate::backend::conv::{ret, ret_owned_fd}; usecrate::fd::{AsFd, AsRawFd, OwnedFd}; usecrate::io; usecrate::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 [`new`]. #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pubstruct CreateFlags: u32 { /// `EPOLL_CLOEXEC` const CLOEXEC = bitcast!(c::EPOLL_CLOEXEC);
/// `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")] pubfn create(flags: CreateFlags) -> io::Result<OwnedFd> { // 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`. /// /// 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 [`wait`]. If a file descriptor is an /// `Arc<dyn SystemResource>`, then `epoll` can be thought to maintain a /// `Weak<dyn SystemResource>` to the file descriptor. #[doc(alias = "epoll_ctl")] pubfn 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`. #[doc(alias = "epoll_ctl")] pubfn 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. #[doc(alias = "epoll_ctl")] pubfn 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. #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] pubfn 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::<c::epoll_event>(),
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`. pubstruct 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<slice::Iter<'a, Event>>,
}
impl<'a> Iterator for Iter<'a> { type Item = Event;
/// 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)] pubstruct 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] pubconstfn new_u64(value: u64) -> Self { Self { as_u64: value }
}
/// Construct a new value containing a `*mut c_void`. #[inline] pubconstfn 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] pubfn 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] pubfn ptr(self) -> *mut c_void { unsafe { self.sixty_four_bit_pointer.pointer }
}
}
/// A vector of `Event`s, plus context for interpreting them. #[cfg(feature = "alloc")] pubstruct EventVec {
events: Vec<Event>,
}
#[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] pubunsafefn 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] pubfn with_capacity(capacity: usize) -> Self { Self {
events: Vec::with_capacity(capacity),
}
}
/// Returns the current `Event` capacity of this `EventVec`. #[inline] pubfn capacity(&self) -> usize { self.events.capacity()
}
/// Reserves enough memory for at least `additional` more `Event`s. #[inline] pubfn reserve(&mutself, additional: usize) { self.events.reserve(additional);
}
/// Reserves enough memory for exactly `additional` more `Event`s. #[inline] pubfn reserve_exact(&mutself, additional: usize) { self.events.reserve_exact(additional);
}
/// Clears all the `Events` out of this `EventVec`. #[inline] pubfn clear(&mutself) { self.events.clear();
}
/// Shrinks the capacity of this `EventVec` as much as possible. #[inline] pubfn shrink_to_fit(&mutself) { self.events.shrink_to_fit();
}
/// Returns an iterator over the `Event`s in this `EventVec`. #[inline] pubfn iter(&self) -> Iter<'_> {
Iter {
iter: self.events.iter().copied(),
}
}
/// Returns the number of `Event`s logically contained in this `EventVec`. #[inline] pubfn len(&mutself) -> usize { self.events.len()
}
/// Tests whether this `EventVec` is logically empty. #[inline] pubfn is_empty(&mutself) -> bool { self.events.is_empty()
}
}
#[cfg(feature = "alloc")] impl<'a> IntoIterator for &'a EventVec { type IntoIter = Iter<'a>; type Item = Event;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.