diff options
Diffstat (limited to 'vendor/rustix/src/event')
| -rw-r--r-- | vendor/rustix/src/event/epoll.rs | 347 | ||||
| -rw-r--r-- | vendor/rustix/src/event/eventfd.rs | 20 | ||||
| -rw-r--r-- | vendor/rustix/src/event/kqueue.rs | 466 | ||||
| -rw-r--r-- | vendor/rustix/src/event/mod.rs | 34 | ||||
| -rw-r--r-- | vendor/rustix/src/event/pause.rs | 31 | ||||
| -rw-r--r-- | vendor/rustix/src/event/poll.rs | 47 | ||||
| -rw-r--r-- | vendor/rustix/src/event/port.rs | 197 | ||||
| -rw-r--r-- | vendor/rustix/src/event/select.rs | 391 |
8 files changed, 1533 insertions, 0 deletions
diff --git a/vendor/rustix/src/event/epoll.rs b/vendor/rustix/src/event/epoll.rs new file mode 100644 index 00000000..839f05de --- /dev/null +++ b/vendor/rustix/src/event/epoll.rs @@ -0,0 +1,347 @@ +//! Linux `epoll` support. +//! +//! # Examples +//! +//! ```no_run +//! # #[cfg(feature = "net")] +//! # fn main() -> std::io::Result<()> { +//! use rustix::buffer::spare_capacity; +//! use rustix::event::epoll; +//! use rustix::fd::AsFd; +//! use rustix::io::{ioctl_fionbio, read, write}; +//! use rustix::net::{ +//! accept, bind, 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(&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 = Vec::with_capacity(4); +//! loop { +//! epoll::wait(&epoll, spare_capacity(&mut event_list), None)?; +//! for event in event_list.drain(..) { +//! 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; +pub use crate::backend::event::epoll::*; +use crate::backend::event::syscalls; +use crate::buffer::Buffer; +use crate::fd::{AsFd, OwnedFd}; +use crate::io; +use crate::timespec::Timespec; +use core::ffi::c_void; +use core::hash::{Hash, Hasher}; + +/// `epoll_create1(flags)`—Creates a new epoll object. +/// +/// Use the [`epoll::CreateFlags::CLOEXEC`] flag to prevent the resulting file +/// descriptor from being implicitly passed across `exec` boundaries. +/// +/// # References +/// - [Linux] +/// - [illumos] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_create.2.html +/// [illumos]: https://www.illumos.org/man/3C/epoll_create +#[inline] +#[doc(alias = "epoll_create1")] +pub fn create(flags: epoll::CreateFlags) -> io::Result<OwnedFd> { + 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`. +/// +/// `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<dyn SystemResource>`, then `epoll` can be thought to +/// maintain a `Weak<dyn SystemResource>` to the file descriptor. Check the +/// [faq] for details. +/// +/// # References +/// - [Linux] +/// - [illumos] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +/// [illumos]: https://www.illumos.org/man/3C/epoll_ctl +/// [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<EpollFd: AsFd, SourceFd: AsFd>( + epoll: EpollFd, + source: SourceFd, + data: epoll::EventData, + event_flags: epoll::EventFlags, +) -> io::Result<()> { + syscalls::epoll_add( + epoll.as_fd(), + source.as_fd(), + &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] +/// - [illumos] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +/// [illumos]: https://www.illumos.org/man/3C/epoll_ctl +#[doc(alias = "epoll_ctl")] +#[inline] +pub fn modify<EpollFd: AsFd, SourceFd: AsFd>( + epoll: EpollFd, + source: SourceFd, + data: epoll::EventData, + event_flags: epoll::EventFlags, +) -> io::Result<()> { + syscalls::epoll_mod( + epoll.as_fd(), + source.as_fd(), + &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] +/// - [illumos] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +/// [illumos]: https://www.illumos.org/man/3C/epoll_ctl +#[doc(alias = "epoll_ctl")] +#[inline] +pub fn delete<EpollFd: AsFd, SourceFd: AsFd>(epoll: EpollFd, source: SourceFd) -> 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`. +/// +/// Linux versions older than 5.11 (those that don't support `epoll_pwait2`) +/// don't support timeouts greater than `c_int::MAX` milliseconds; if an +/// unsupported timeout is passed, this function fails with +/// [`io::Errno::INVAL`]. Enable the "linux_5_11" feature to enable the full +/// range of timeouts. +/// +/// # References +/// - [Linux] +/// - [illumos] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html +/// [illumos]: https://www.illumos.org/man/3C/epoll_wait +#[doc(alias = "epoll_wait")] +#[inline] +pub fn wait<EpollFd: AsFd, Buf: Buffer<Event>>( + epoll: EpollFd, + mut event_list: Buf, + timeout: Option<&Timespec>, +) -> io::Result<Buf::Output> { + // SAFETY: `epoll_wait` behaves. + let nfds = unsafe { syscalls::epoll_wait(epoll.as_fd(), event_list.parts_mut(), timeout)? }; + // SAFETY: `epoll_wait` behaves. + unsafe { Ok(event_list.assume_init(nfds)) } +} + +/// 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) +)] +#[cfg_attr( + all(solarish, any(target_arch = "x86", target_arch = "x86_64")), + repr(packed(4)) +)] +#[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 [`epoll::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<H: Hasher>(&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, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::backend::c; + + #[test] + fn test_epoll_layouts() { + 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/vendor/rustix/src/event/eventfd.rs b/vendor/rustix/src/event/eventfd.rs new file mode 100644 index 00000000..a76f2cfc --- /dev/null +++ b/vendor/rustix/src/event/eventfd.rs @@ -0,0 +1,20 @@ +use crate::fd::OwnedFd; +use crate::{backend, io}; + +pub use backend::event::types::EventfdFlags; + +/// `eventfd(initval, flags)`—Creates a file descriptor for event +/// notification. +/// +/// # References +/// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/eventfd.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?eventfd +/// [illumos]: https://illumos.org/man/3C/eventfd +#[inline] +pub fn eventfd(initval: u32, flags: EventfdFlags) -> io::Result<OwnedFd> { + backend::event::syscalls::eventfd(initval, flags) +} diff --git a/vendor/rustix/src/event/kqueue.rs b/vendor/rustix/src/event/kqueue.rs new file mode 100644 index 00000000..897d9398 --- /dev/null +++ b/vendor/rustix/src/event/kqueue.rs @@ -0,0 +1,466 @@ +//! An API for interfacing with `kqueue`. + +use crate::buffer::Buffer; +use crate::fd::{AsFd, OwnedFd, RawFd}; +use crate::pid::Pid; +use crate::signal::Signal; +use crate::timespec::Timespec; +use crate::{backend, io}; + +use backend::c::{self, intptr_t, kevent as kevent_t, uintptr_t}; +use backend::event::syscalls; + +use core::mem::zeroed; +use core::time::Duration; + +/// A `kqueue` event for use with [`kevent`]. +#[repr(transparent)] +#[derive(Copy, Clone)] +pub struct Event { + // The layout varies between BSDs and macOS. + inner: kevent_t, +} + +impl Event { + /// Create a new `Event`. + #[allow(clippy::needless_update)] + pub fn new(filter: EventFilter, flags: EventFlags, udata: *mut c::c_void) -> Event { + let (ident, data, filter, fflags) = match filter { + EventFilter::Read(fd) => (fd as uintptr_t, 0, c::EVFILT_READ, 0), + EventFilter::Write(fd) => (fd as _, 0, c::EVFILT_WRITE, 0), + #[cfg(target_os = "freebsd")] + EventFilter::Empty(fd) => (fd as _, 0, c::EVFILT_EMPTY, 0), + EventFilter::Vnode { vnode, flags } => (vnode as _, 0, c::EVFILT_VNODE, flags.bits()), + EventFilter::Proc { pid, flags } => { + (Pid::as_raw(Some(pid)) as _, 0, c::EVFILT_PROC, flags.bits()) + } + EventFilter::Signal { signal, times: _ } => { + (signal.as_raw() as _, 0, c::EVFILT_SIGNAL, 0) + } + EventFilter::Timer { ident, timer } => { + #[cfg(any(apple, target_os = "freebsd", target_os = "netbsd"))] + let (data, fflags) = match timer { + Some(timer) => { + if timer.subsec_millis() == 0 { + (timer.as_secs() as _, c::NOTE_SECONDS) + } else if timer.subsec_nanos() == 0 { + (timer.as_micros() as _, c::NOTE_USECONDS) + } else { + (timer.as_nanos() as _, c::NOTE_NSECONDS) + } + } + None => (intptr_t::MAX, c::NOTE_SECONDS), + }; + #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))] + let (data, fflags) = match timer { + Some(timer) => (timer.as_millis() as _, 0), + None => (intptr_t::MAX, 0), + }; + + (ident as _, data, c::EVFILT_TIMER, fflags) + } + #[cfg(any(apple, freebsdlike))] + EventFilter::User { + ident, + flags, + user_flags, + } => (ident as _, 0, c::EVFILT_USER, flags.bits() | user_flags.0), + EventFilter::Unknown => panic!("unknown filter"), + }; + + Event { + inner: kevent_t { + ident, + filter: filter as _, + flags: flags.bits() as _, + fflags, + data: { + // On OpenBSD, data is an `i64` and not an `isize`. + data as _ + }, + udata: { + // On NetBSD, udata is an `isize` and not a pointer. + udata as _ + }, + ..unsafe { zeroed() } + }, + } + } + + /// Get the event flags for this event. + pub fn flags(&self) -> EventFlags { + EventFlags::from_bits_retain(self.inner.flags as _) + } + + /// Get the user data for this event. + pub fn udata(&self) -> *mut c::c_void { + // On NetBSD, udata is an isize and not a pointer. + self.inner.udata as _ + } + + /// Get the raw data for this event. + pub fn data(&self) -> i64 { + // On some BSDs, data is an `isize` and not an `i64`. + self.inner.data as _ + } + + /// Get the filter of this event. + pub fn filter(&self) -> EventFilter { + match self.inner.filter as _ { + c::EVFILT_READ => EventFilter::Read(self.inner.ident as _), + c::EVFILT_WRITE => EventFilter::Write(self.inner.ident as _), + #[cfg(target_os = "freebsd")] + c::EVFILT_EMPTY => EventFilter::Empty(self.inner.ident as _), + c::EVFILT_VNODE => EventFilter::Vnode { + vnode: self.inner.ident as _, + flags: VnodeEvents::from_bits_retain(self.inner.fflags), + }, + c::EVFILT_PROC => EventFilter::Proc { + pid: Pid::from_raw(self.inner.ident as _).unwrap(), + flags: ProcessEvents::from_bits_retain(self.inner.fflags), + }, + c::EVFILT_SIGNAL => EventFilter::Signal { + // SAFETY: `EventFilter::new` requires a valid `Signal`. + signal: unsafe { Signal::from_raw_unchecked(self.inner.ident as _) }, + times: self.inner.data as _, + }, + c::EVFILT_TIMER => EventFilter::Timer { + ident: self.inner.ident as _, + timer: { + let (data, fflags) = (self.inner.data, self.inner.fflags); + #[cfg(not(any(apple, target_os = "freebsd", target_os = "netbsd")))] + let _ = fflags; + #[cfg(any(apple, target_os = "freebsd", target_os = "netbsd"))] + match fflags as _ { + c::NOTE_SECONDS => Some(Duration::from_secs(data as _)), + c::NOTE_USECONDS => Some(Duration::from_micros(data as _)), + c::NOTE_NSECONDS => Some(Duration::from_nanos(data as _)), + _ => { + // Unknown timer flags. + None + } + } + #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))] + Some(Duration::from_millis(data as _)) + }, + }, + #[cfg(any(apple, freebsdlike))] + c::EVFILT_USER => EventFilter::User { + ident: self.inner.ident as _, + flags: UserFlags::from_bits_retain(self.inner.fflags), + user_flags: UserDefinedFlags(self.inner.fflags & EVFILT_USER_FLAGS), + }, + _ => EventFilter::Unknown, + } + } +} + +/// Bottom 24 bits of a `u32`. +#[cfg(any(apple, freebsdlike))] +const EVFILT_USER_FLAGS: u32 = 0x00ff_ffff; + +/// The possible filters for a `kqueue`. +#[repr(i16)] +#[non_exhaustive] +pub enum EventFilter { + /// A read filter. + Read(RawFd), + + /// A write filter. + Write(RawFd), + + /// An empty filter. + #[cfg(target_os = "freebsd")] + Empty(RawFd), + + /// A VNode filter. + Vnode { + /// The file descriptor we looked for events in. + vnode: RawFd, + + /// The flags for this event. + flags: VnodeEvents, + }, + + /// A process filter. + Proc { + /// The process ID we waited on. + pid: Pid, + + /// The flags for this event. + flags: ProcessEvents, + }, + + /// A signal filter. + Signal { + /// The signal number we waited on. + signal: Signal, + + /// The number of times the signal has been received since the last + /// call to kevent. + times: usize, + }, + + /// A timer filter. + Timer { + /// The identifier for this event. + ident: intptr_t, + + /// The duration for this event. + timer: Option<Duration>, + }, + + /// A user filter. + #[cfg(any(apple, freebsdlike))] + User { + /// The identifier for this event. + ident: intptr_t, + + /// The flags for this event. + flags: UserFlags, + + /// The user-defined flags for this event. + user_flags: UserDefinedFlags, + }, + + /// This filter is unknown. + /// + /// # Panics + /// + /// Passing this into `Event::new()` will result in a panic. + Unknown, +} + +bitflags::bitflags! { + /// The flags for a `kqueue` event specifying actions to perform. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct EventFlags: u16 { + /// Add the event to the `kqueue`. + const ADD = c::EV_ADD as _; + + /// Enable the event. + const ENABLE = c::EV_ENABLE as _; + + /// Disable the event. + const DISABLE = c::EV_DISABLE as _; + + /// Delete the event from the `kqueue`. + const DELETE = c::EV_DELETE as _; + + /// TODO + const RECEIPT = c::EV_RECEIPT as _; + + /// Clear the event after it is triggered. + const ONESHOT = c::EV_ONESHOT as _; + + /// TODO + const CLEAR = c::EV_CLEAR as _; + + /// TODO + const EOF = c::EV_EOF as _; + + /// TODO + const ERROR = c::EV_ERROR as _; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +bitflags::bitflags! { + /// The flags for a virtual node event. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct VnodeEvents: u32 { + /// The file was deleted. + const DELETE = c::NOTE_DELETE; + + /// The file was written to. + const WRITE = c::NOTE_WRITE; + + /// The file was extended. + const EXTEND = c::NOTE_EXTEND; + + /// The file had its attributes changed. + const ATTRIBUTES = c::NOTE_ATTRIB; + + /// The file was renamed. + const RENAME = c::NOTE_RENAME; + + /// Access to the file was revoked. + const REVOKE = c::NOTE_REVOKE; + + /// The link count of the file has changed. + const LINK = c::NOTE_LINK; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +bitflags::bitflags! { + /// The flags for a process event. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ProcessEvents: u32 { + /// The process exited. + const EXIT = c::NOTE_EXIT; + + /// The process forked itself. + const FORK = c::NOTE_FORK; + + /// The process executed a new process. + const EXEC = c::NOTE_EXEC; + + /// Follow the process through `fork` calls (write only). + const TRACK = c::NOTE_TRACK; + + /// An error has occurred with following the process. + const TRACKERR = c::NOTE_TRACKERR; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(any(apple, freebsdlike))] +bitflags::bitflags! { + /// The flags for a user event. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct UserFlags: u32 { + /// Ignore the user input flags. + #[doc(alias = "NOP")] + const NOINPUT = c::NOTE_FFNOP; + + /// Bitwise AND `fflags`. + const AND = c::NOTE_FFAND; + + /// Bitwise OR `fflags`. + const OR = c::NOTE_FFOR; + + /// Copy `fflags`. + const COPY = c::NOTE_FFCOPY; + + /// Control mask for operations. + const CTRLMASK = c::NOTE_FFCTRLMASK; + + /// User defined flags for masks. + const UDFMASK = c::NOTE_FFLAGSMASK; + + /// Trigger the event. + const TRIGGER = c::NOTE_TRIGGER; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// User-defined flags. +/// +/// Only the lower 24 bits are used in this struct. +#[repr(transparent)] +#[cfg(any(apple, freebsdlike))] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct UserDefinedFlags(u32); + +#[cfg(any(apple, freebsdlike))] +impl UserDefinedFlags { + /// Create a new `UserDefinedFlags` from a `u32`. + pub fn new(flags: u32) -> Self { + Self(flags & EVFILT_USER_FLAGS) + } + + /// Get the underlying `u32`. + pub fn get(self) -> u32 { + self.0 + } +} + +/// `kqueue()`—Create a new `kqueue` file descriptor. +/// +/// # References +/// - [Apple] +/// - [FreeBSD] +/// - [OpenBSD] +/// - [NetBSD] +/// - [DragonFly BSD] +/// +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2 +/// [OpenBSD]: https://man.openbsd.org/kqueue.2 +/// [NetBSD]: https://man.netbsd.org/kqueue.2 +/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=kqueue§ion=2 +pub fn kqueue() -> io::Result<OwnedFd> { + syscalls::kqueue() +} + +/// `kevent(kqueue, changelist, eventlist, timeout)`—Wait for events on a +/// `kqueue`. +/// +/// If an unsupported timeout is passed, this function fails with +/// [`io::Errno::INVAL`]. +/// +/// # Safety +/// +/// The file descriptors referred to by the `Event` structs must be valid for +/// the lifetime of the `kqueue` file descriptor. +/// +/// # References +/// - [Apple] +/// - [FreeBSD] +/// - [OpenBSD] +/// - [NetBSD] +/// - [DragonFly BSD] +/// +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kevent.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=kevent&sektion=2 +/// [OpenBSD]: https://man.openbsd.org/kevent.2 +/// [NetBSD]: https://man.netbsd.org/kevent.2 +/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=kevent§ion=2 +pub unsafe fn kevent_timespec<Fd: AsFd, Buf: Buffer<Event>>( + kqueue: Fd, + changelist: &[Event], + mut eventlist: Buf, + timeout: Option<&Timespec>, +) -> io::Result<Buf::Output> { + // Populate the event list with events. + let len = syscalls::kevent(kqueue.as_fd(), changelist, eventlist.parts_mut(), timeout) + .map(|res| res as _)?; + + Ok(eventlist.assume_init(len)) +} + +/// `kevent(kqueue, changelist, eventlist, timeout)`—Wait for events on a +/// `kqueue`. +/// +/// This is a wrapper around [`kevent_timespec`] which takes a `Duration` +/// instead of a `Timespec` for the timemout value. `Timespec` has a signed +/// `i64` seconds field; if converting `Duration` to `Timespec` overflows, +/// `None` is passed as the timeout instead, such such a large timeout would +/// be effectively infinite in practice. +/// +/// # Safety +/// +/// The file descriptors referred to by the `Event` structs must be valid for +/// the lifetime of the `kqueue` file descriptor. +pub unsafe fn kevent<Fd: AsFd, Buf: Buffer<Event>>( + kqueue: Fd, + changelist: &[Event], + eventlist: Buf, + timeout: Option<Duration>, +) -> io::Result<Buf::Output> { + let timeout = match timeout { + Some(timeout) => match timeout.as_secs().try_into() { + Ok(tv_sec) => Some(Timespec { + tv_sec, + tv_nsec: timeout.subsec_nanos() as _, + }), + Err(_) => None, + }, + None => None, + }; + + kevent_timespec(kqueue, changelist, eventlist, timeout.as_ref()) +} diff --git a/vendor/rustix/src/event/mod.rs b/vendor/rustix/src/event/mod.rs new file mode 100644 index 00000000..29dfc227 --- /dev/null +++ b/vendor/rustix/src/event/mod.rs @@ -0,0 +1,34 @@ +//! Event operations. + +#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))] +pub mod epoll; +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "illumos", + target_os = "espidf" +))] +mod eventfd; +#[cfg(bsd)] +pub mod kqueue; +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +mod pause; +mod poll; +#[cfg(solarish)] +pub mod port; +#[cfg(any(bsd, linux_kernel, windows, target_os = "wasi"))] +mod select; + +pub use crate::timespec::{Nsecs, Secs, Timespec}; +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "illumos", + target_os = "espidf" +))] +pub use eventfd::{eventfd, EventfdFlags}; +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +pub use pause::*; +pub use poll::{poll, PollFd, PollFlags}; +#[cfg(any(bsd, linux_kernel, windows, target_os = "wasi"))] +pub use select::*; diff --git a/vendor/rustix/src/event/pause.rs b/vendor/rustix/src/event/pause.rs new file mode 100644 index 00000000..41103011 --- /dev/null +++ b/vendor/rustix/src/event/pause.rs @@ -0,0 +1,31 @@ +use crate::backend; + +/// `pause()`—Sleep until interrupted by a signal. +/// +/// The POSIX `pause` interface returns an error code, but the only thing +/// `pause` does is sleep until interrupted by a signal. If it were exposed in +/// the API here it would always return `Errno::INTR`, so for simplicity the +/// return value is omitted. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [Apple] +/// - [FreeBSD] +/// - [NetBSD] +/// - [OpenBSD] +/// - [DragonFly BSD] +/// - [illumos] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/pause.html +/// [Linux]: https://man7.org/linux/man-pages/man2/pause.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/pause.3.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=pause&sektion=3 +/// [NetBSD]: https://man.netbsd.org/pause.3 +/// [OpenBSD]: https://man.openbsd.org/pause.3 +/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=pause§ion=3 +/// [illumos]: https://illumos.org/man/2/pause +#[inline] +pub fn pause() { + backend::event::syscalls::pause() +} diff --git a/vendor/rustix/src/event/poll.rs b/vendor/rustix/src/event/poll.rs new file mode 100644 index 00000000..bf25fd5d --- /dev/null +++ b/vendor/rustix/src/event/poll.rs @@ -0,0 +1,47 @@ +use crate::event::Timespec; +use crate::{backend, io}; + +pub use backend::event::poll_fd::{PollFd, PollFlags}; + +/// `poll(self.fds, timeout)`—Wait for events on lists of file descriptors. +/// +/// Some platforms (those that don't support `ppoll`) don't support timeouts +/// greater than `c_int::MAX` milliseconds; if an unsupported timeout is +/// passed, this function fails with [`io::Errno::INVAL`]. +/// +/// On macOS, `poll` doesn't work on fds for /dev/tty or /dev/null, however +/// [`select`] is available and does work on these fds. +/// +/// [`select`]: crate::event::select() +/// +/// This function does not use the [`Buffer`] trait because the `fds` list is +/// both an input and output buffer. +/// +/// [`Buffer`]: crate::buffer::Buffer +/// +/// # References +/// - [Beej's Guide to Network Programming] +/// - [POSIX] +/// - [Linux] +/// - [Apple] +/// - [Winsock] +/// - [FreeBSD] +/// - [NetBSD] +/// - [OpenBSD] +/// - [DragonFly BSD] +/// - [illumos] +/// +/// [Beej's Guide to Network Programming]: https://beej.us/guide/bgnet/html/split/slightly-advanced-techniques.html#poll +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/poll.html +/// [Linux]: https://man7.org/linux/man-pages/man2/poll.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/poll.2.html +/// [Winsock]: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=poll&sektion=2 +/// [NetBSD]: https://man.netbsd.org/poll.2 +/// [OpenBSD]: https://man.openbsd.org/poll.2 +/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=poll§ion=2 +/// [illumos]: https://illumos.org/man/2/poll +#[inline] +pub fn poll(fds: &mut [PollFd<'_>], timeout: Option<&Timespec>) -> io::Result<usize> { + backend::event::syscalls::poll(fds, timeout) +} diff --git a/vendor/rustix/src/event/port.rs b/vendor/rustix/src/event/port.rs new file mode 100644 index 00000000..7cc5f939 --- /dev/null +++ b/vendor/rustix/src/event/port.rs @@ -0,0 +1,197 @@ +//! Solaris/illumos event ports. +//! +//! # Examples +//! +//! ``` +//! # fn test() -> std::io::Result<()> { +//! use rustix::event::port; +//! use rustix::stdio::stdout; +//! use std::io; +//! +//! let some_fd = stdout(); +//! let some_userdata = 7 as *mut _; +//! +//! // Create a port. +//! let port = port::create()?; +//! +//! // Associate `some_fd` with the port. +//! unsafe { +//! port::associate_fd(&port, some_fd, port::PollFlags::IN, some_userdata)?; +//! } +//! +//! // Get a single event. +//! let event = port::get(&port, None)?; +//! +//! assert_eq!(event.userdata(), some_userdata); +//! # Ok(()) +//! # } +//! ``` + +use crate::backend::c; +use crate::backend::event::syscalls; +use crate::buffer::Buffer; +use crate::fd::{AsFd, AsRawFd, OwnedFd}; +use crate::timespec::Timespec; +use crate::{ffi, io}; + +pub use super::PollFlags; + +/// The structure representing a port event. +#[repr(transparent)] +#[doc(alias = "port_event")] +pub struct Event(pub(crate) c::port_event); + +impl Event { + /// Get the events associated with this event. + pub fn events(&self) -> i32 { + self.0.portev_events + } + + /// Get the event source associated with this event. + pub fn object(&self) -> usize { + self.0.portev_object + } + + /// Get the userdata associated with this event. + pub fn userdata(&self) -> *mut ffi::c_void { + self.0.portev_user + } +} + +/// `port_create()`—Creates a new port. +/// +/// # References +/// - [OpenSolaris] +/// - [illumos] +/// +/// [OpenSolaris]: https://www.unix.com/man-page/opensolaris/3C/port_create/ +/// [illumos]: https://illumos.org/man/3C/port_create +#[doc(alias = "port_create")] +pub fn create() -> io::Result<OwnedFd> { + syscalls::port_create() +} + +/// `port_associate(_, PORT_SOURCE_FD, _, _, _)`—Associates a file descriptor +/// with a port. +/// +/// # Safety +/// +/// Any `object`s passed into the `port` must be valid for the lifetime of the +/// `port`. Logically, `port` keeps a borrowed reference to the `object` until +/// it is removed via [`dissociate_fd`]. +/// +/// # References +/// - [OpenSolaris] +/// - [illumos] +/// +/// [OpenSolaris]: https://www.unix.com/man-page/opensolaris/3C/port_associate/ +/// [illumos]: https://illumos.org/man/3C/port_associate +#[doc(alias = "port_associate")] +pub unsafe fn associate_fd<Fd: AsFd, RawFd: AsRawFd>( + port: Fd, + object: RawFd, + events: PollFlags, + userdata: *mut ffi::c_void, +) -> io::Result<()> { + syscalls::port_associate( + port.as_fd(), + c::PORT_SOURCE_FD, + object.as_raw_fd() as _, + events.bits() as _, + userdata.cast(), + ) +} + +/// `port_dissociate(_, PORT_SOURCE_FD, _)`—Dissociates a file descriptor +/// from a port. +/// +/// # Safety +/// +/// The file descriptor passed into this function must have been previously +/// associated with the port via [`associate_fd`]. +/// +/// # References +/// - [OpenSolaris] +/// - [illumos] +/// +/// [OpenSolaris]: https://www.unix.com/man-page/opensolaris/3C/port_dissociate +/// [illumos]: https://illumos.org/man/3C/port_dissociate +#[doc(alias = "port_dissociate")] +pub unsafe fn dissociate_fd<Fd: AsFd, RawFd: AsRawFd>(port: Fd, object: RawFd) -> io::Result<()> { + syscalls::port_dissociate(port.as_fd(), c::PORT_SOURCE_FD, object.as_raw_fd() as _) +} + +/// `port_get(port, timeout)`—Gets an event from a port. +/// +/// If an unsupported timeout is passed, this function fails with +/// [`io::Errno::INVAL`]. +/// +/// # References +/// - [OpenSolaris] +/// - [illumos] +/// +/// [OpenSolaris]: https://www.unix.com/man-page/opensolaris/3C/port_get/ +/// [illumos]: https://illumos.org/man/3C/port_get +#[doc(alias = "port_get")] +pub fn get<Fd: AsFd>(port: Fd, timeout: Option<&Timespec>) -> io::Result<Event> { + syscalls::port_get(port.as_fd(), timeout) +} + +/// `port_getn(port, events, min_events, timeout)`—Gets multiple events from +/// a port. +/// +/// If `events` is empty, this does nothing and returns immediately. +/// +/// To query the number of events without retrieving any, use [`getn_query`]. +/// +/// If an unsupported timeout is passed, this function fails with +/// [`io::Errno::INVAL`]. +/// +/// # References +/// - [OpenSolaris] +/// - [illumos] +/// +/// [OpenSolaris]: https://www.unix.com/man-page/opensolaris/3C/port_getn/ +/// [illumos]: https://illumos.org/man/3C/port_getn +#[doc(alias = "port_getn")] +pub fn getn<Fd: AsFd, Buf: Buffer<Event>>( + port: Fd, + mut events: Buf, + min_events: u32, + timeout: Option<&Timespec>, +) -> io::Result<Buf::Output> { + // SAFETY: `port_getn` behaves. + let nevents = + unsafe { syscalls::port_getn(port.as_fd(), events.parts_mut(), min_events, timeout)? }; + // SAFETY: `port_getn` behaves. + unsafe { Ok(events.assume_init(nevents)) } +} + +/// `port_getn(port, NULL, 0, NULL)`—Queries the number of events +/// available from a port. +/// +/// To retrieve the events, use [`getn`]. +/// +/// # References +/// - [OpenSolaris] +/// - [illumos] +/// +/// [OpenSolaris]: https://www.unix.com/man-page/opensolaris/3C/port_getn/ +/// [illumos]: https://illumos.org/man/3C/port_getn +#[doc(alias = "port_getn")] +pub fn getn_query<Fd: AsFd>(port: Fd) -> io::Result<u32> { + syscalls::port_getn_query(port.as_fd()) +} + +/// `port_send(port, events, userdata)`—Sends an event to a port. +/// +/// # References +/// - [OpenSolaris] +/// - [illumos] +/// +/// [OpenSolaris]: https://www.unix.com/man-page/opensolaris/3C/port_send/ +/// [illumos]: https://illumos.org/man/3C/port_send +#[doc(alias = "port_send")] +pub fn send<Fd: AsFd>(port: Fd, events: i32, userdata: *mut ffi::c_void) -> io::Result<()> { + syscalls::port_send(port.as_fd(), events, userdata.cast()) +} diff --git a/vendor/rustix/src/event/select.rs b/vendor/rustix/src/event/select.rs new file mode 100644 index 00000000..6bb93b53 --- /dev/null +++ b/vendor/rustix/src/event/select.rs @@ -0,0 +1,391 @@ +//! The `select` function. +//! +//! # Safety +//! +//! `select` is unsafe due to I/O safety. +#![allow(unsafe_code)] + +#[cfg(any(linux_like, target_os = "wasi"))] +use crate::backend::c; +use crate::event::Timespec; +use crate::fd::RawFd; +use crate::{backend, io}; +#[cfg(any(windows, target_os = "wasi"))] +use core::mem::align_of; +use core::mem::size_of; + +/// wasi-libc's `fd_set` type. The libc bindings for it have private fields, so +/// we redeclare it for ourselves so that we can access the fields. They're +/// publicly exposed in wasi-libc. +#[cfg(target_os = "wasi")] +#[repr(C)] +struct FD_SET { + /// The wasi-libc headers call this `__nfds`. + fd_count: usize, + /// The wasi-libc headers call this `__fds`. + fd_array: [i32; c::FD_SETSIZE], +} + +#[cfg(windows)] +use windows_sys::Win32::Networking::WinSock::FD_SET; + +/// Storage element type for use with [`select`]. +#[cfg(all( + target_pointer_width = "64", + any(windows, target_os = "freebsd", target_os = "dragonfly") +))] +#[repr(transparent)] +#[derive(Copy, Clone, Default)] +pub struct FdSetElement(pub(crate) u64); + +/// Storage element type for use with [`select`]. +#[cfg(linux_like)] +#[repr(transparent)] +#[derive(Copy, Clone, Default)] +pub struct FdSetElement(pub(crate) c::c_ulong); + +/// Storage element type for use with [`select`]. +#[cfg(not(any( + linux_like, + target_os = "wasi", + all( + target_pointer_width = "64", + any(windows, target_os = "freebsd", target_os = "dragonfly") + ) +)))] +#[repr(transparent)] +#[derive(Copy, Clone, Default)] +pub struct FdSetElement(pub(crate) u32); + +/// Storage element type for use with [`select`]. +#[cfg(target_os = "wasi")] +#[repr(transparent)] +#[derive(Copy, Clone, Default)] +pub struct FdSetElement(pub(crate) usize); + +/// `select(nfds, readfds, writefds, exceptfds, timeout)`—Wait for events on +/// sets of file descriptors. +/// +/// `readfds`, `writefds`, `exceptfds` must point to arrays of `FdSetElement` +/// containing at least `nfds.div_ceil(size_of::<FdSetElement>())` elements. +/// +/// If an unsupported timeout is passed, this function fails with +/// [`io::Errno::INVAL`]. +/// +/// This `select` wrapper differs from POSIX in that `nfds` is not limited to +/// `FD_SETSIZE`. Instead of using the fixed-sized `fd_set` type, this function +/// takes raw pointers to arrays of `fd_set_num_elements(max_fd + 1, num_fds)`, +/// where `max_fd` is the maximum value of any fd that will be inserted into +/// the set, and `num_fds` is the maximum number of fds that will be inserted +/// into the set. +/// +/// In particular, on Apple platforms, this function behaves as if +/// `_DARWIN_UNLIMITED_SELECT` were predefined. +/// +/// On illumos, this function is not defined because the `select` function on +/// this platform always has an `FD_SETSIZE` limitation, following POSIX. This +/// platform's documentation recommends using [`poll`] instead. +/// +/// [`fd_set_insert`], [`fd_set_remove`], and [`FdSetIter`] are provided for +/// setting, clearing, and iterating with sets. +/// +/// [`poll`]: crate::event::poll() +/// +/// # Safety +/// +/// All fds in all the sets must correspond to open file descriptors. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [Apple] +/// - [FreeBSD] +/// - [NetBSD] +/// - [OpenBSD] +/// - [DragonFly BSD] +/// - [Winsock] +/// - [glibc] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/select.html +/// [Linux]: https://man7.org/linux/man-pages/man2/select.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/select.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=select&sektion=2 +/// [NetBSD]: https://man.netbsd.org/select.2 +/// [OpenBSD]: https://man.openbsd.org/select.2 +/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=select§ion=2 +/// [Winsock]: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-select +/// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/Waiting-for-I_002fO.html#index-select +pub unsafe fn select( + nfds: i32, + readfds: Option<&mut [FdSetElement]>, + writefds: Option<&mut [FdSetElement]>, + exceptfds: Option<&mut [FdSetElement]>, + timeout: Option<&Timespec>, +) -> io::Result<i32> { + backend::event::syscalls::select(nfds, readfds, writefds, exceptfds, timeout) +} + +#[cfg(not(any(windows, target_os = "wasi")))] +const BITS: usize = size_of::<FdSetElement>() * 8; + +/// Set `fd` in the set pointed to by `fds`. +#[doc(alias = "FD_SET")] +#[inline] +pub fn fd_set_insert(fds: &mut [FdSetElement], fd: RawFd) { + #[cfg(not(any(windows, target_os = "wasi")))] + { + let fd = fd as usize; + fds[fd / BITS].0 |= 1 << (fd % BITS); + } + + #[cfg(any(windows, target_os = "wasi"))] + { + let set = unsafe { &mut *fds.as_mut_ptr().cast::<FD_SET>() }; + let fd_count = set.fd_count; + let fd_array = &set.fd_array[..fd_count as usize]; + + if !fd_array.contains(&(fd as _)) { + let fd_array = &mut set.fd_array[..fd_count as usize + 1]; + set.fd_count = fd_count + 1; + fd_array[fd_count as usize] = fd as _; + } + } +} + +/// Clear `fd` in the set pointed to by `fds`. +#[doc(alias = "FD_CLR")] +#[inline] +pub fn fd_set_remove(fds: &mut [FdSetElement], fd: RawFd) { + #[cfg(not(any(windows, target_os = "wasi")))] + { + let fd = fd as usize; + fds[fd / BITS].0 &= !(1 << (fd % BITS)); + } + + #[cfg(any(windows, target_os = "wasi"))] + { + let set = unsafe { &mut *fds.as_mut_ptr().cast::<FD_SET>() }; + let fd_count = set.fd_count; + let fd_array = &set.fd_array[..fd_count as usize]; + + if let Some(pos) = fd_array.iter().position(|p| *p as RawFd == fd) { + set.fd_count = fd_count - 1; + set.fd_array[pos] = *set.fd_array.last().unwrap(); + } + } +} + +/// Compute the minimum `nfds` value needed for the set pointed to by `fds`. +#[inline] +pub fn fd_set_bound(fds: &[FdSetElement]) -> RawFd { + #[cfg(not(any(windows, target_os = "wasi")))] + { + if let Some(position) = fds.iter().rposition(|element| element.0 != 0) { + let element = fds[position].0; + (position * BITS + (BITS - element.leading_zeros() as usize)) as RawFd + } else { + 0 + } + } + + #[cfg(any(windows, target_os = "wasi"))] + { + let set = unsafe { &*fds.as_ptr().cast::<FD_SET>() }; + let fd_count = set.fd_count; + let fd_array = &set.fd_array[..fd_count as usize]; + let mut max = 0; + for fd in fd_array { + if *fd >= max { + max = *fd + 1; + } + } + max as RawFd + } +} + +/// Compute the number of `FdSetElement`s needed to hold a set which can +/// contain up to `set_count` file descriptors with values less than `nfds`. +#[inline] +pub fn fd_set_num_elements(set_count: usize, nfds: RawFd) -> usize { + #[cfg(any(windows, target_os = "wasi"))] + { + let _ = nfds; + + fd_set_num_elements_for_fd_array(set_count) + } + + #[cfg(not(any(windows, target_os = "wasi")))] + { + let _ = set_count; + + fd_set_num_elements_for_bitvector(nfds) + } +} + +/// `fd_set_num_elements` implementation on platforms with fd array +/// implementations. +#[cfg(any(windows, target_os = "wasi"))] +#[inline] +pub(crate) fn fd_set_num_elements_for_fd_array(set_count: usize) -> usize { + // Ensure that we always have a big enough set to dereference an `FD_SET`. + core::cmp::max( + fd_set_num_elements_for_fd_array_raw(set_count), + div_ceil(size_of::<FD_SET>(), size_of::<FdSetElement>()), + ) +} + +/// Compute the raw `fd_set_num_elements` value, before ensuring the value is +/// big enough to dereference an `FD_SET`. +#[cfg(any(windows, target_os = "wasi"))] +#[inline] +fn fd_set_num_elements_for_fd_array_raw(set_count: usize) -> usize { + // Allocate space for an `fd_count` field, plus `set_count` elements + // for the `fd_array` field. + div_ceil( + core::cmp::max(align_of::<FD_SET>(), align_of::<RawFd>()) + set_count * size_of::<RawFd>(), + size_of::<FdSetElement>(), + ) +} + +/// `fd_set_num_elements` implementation on platforms with bitvector +/// implementations. +#[cfg(not(any(windows, target_os = "wasi")))] +#[inline] +pub(crate) fn fd_set_num_elements_for_bitvector(nfds: RawFd) -> usize { + // Allocate space for a dense bitvector for `nfds` bits. + let nfds = nfds as usize; + div_ceil(nfds, BITS) +} + +fn div_ceil(lhs: usize, rhs: usize) -> usize { + let d = lhs / rhs; + let r = lhs % rhs; + if r > 0 { + d + 1 + } else { + d + } +} + +/// An iterator over the fds in a set. +#[doc(alias = "FD_ISSET")] +#[cfg(not(any(windows, target_os = "wasi")))] +pub struct FdSetIter<'a> { + current: RawFd, + fds: &'a [FdSetElement], +} + +/// An iterator over the fds in a set. +#[doc(alias = "FD_ISSET")] +#[cfg(any(windows, target_os = "wasi"))] +pub struct FdSetIter<'a> { + current: usize, + fds: &'a [FdSetElement], +} + +impl<'a> FdSetIter<'a> { + /// Construct a `FdSetIter` for the given set. + pub fn new(fds: &'a [FdSetElement]) -> Self { + Self { current: 0, fds } + } +} + +#[cfg(not(any(windows, target_os = "wasi")))] +impl<'a> Iterator for FdSetIter<'a> { + type Item = RawFd; + + fn next(&mut self) -> Option<Self::Item> { + if let Some(element) = self.fds.get(self.current as usize / BITS) { + // Test whether the current element has more bits set. + let shifted = element.0 >> ((self.current as usize % BITS) as u32); + if shifted != 0 { + let fd = self.current + shifted.trailing_zeros() as RawFd; + self.current = fd + 1; + return Some(fd); + } + + // Search through the array for the next element with bits set. + if let Some(index) = self.fds[(self.current as usize / BITS) + 1..] + .iter() + .position(|element| element.0 != 0) + { + let index = index + (self.current as usize / BITS) + 1; + let element = self.fds[index].0; + let fd = (index * BITS) as RawFd + element.trailing_zeros() as RawFd; + self.current = fd + 1; + return Some(fd); + } + } + None + } +} + +#[cfg(any(windows, target_os = "wasi"))] +impl<'a> Iterator for FdSetIter<'a> { + type Item = RawFd; + + fn next(&mut self) -> Option<Self::Item> { + let current = self.current; + + let set = unsafe { &*self.fds.as_ptr().cast::<FD_SET>() }; + let fd_count = set.fd_count; + let fd_array = &set.fd_array[..fd_count as usize]; + + if current == fd_count as usize { + return None; + } + let fd = fd_array[current as usize]; + self.current = current + 1; + Some(fd as RawFd) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::mem::{align_of, size_of}; + + #[test] + #[cfg(any(windows, target_os = "wasi"))] + fn layouts() { + // The `FdSetElement` array should be suitably aligned. + assert_eq!(align_of::<FdSetElement>(), align_of::<FD_SET>()); + + // The layout of `FD_SET` should match our layout of a set of the same + // size. + assert_eq!( + fd_set_num_elements_for_fd_array_raw( + memoffset::span_of!(FD_SET, fd_array).len() / size_of::<RawFd>() + ) * size_of::<FdSetElement>(), + size_of::<FD_SET>() + ); + assert_eq!( + fd_set_num_elements_for_fd_array( + memoffset::span_of!(FD_SET, fd_array).len() / size_of::<RawFd>() + ) * size_of::<FdSetElement>(), + size_of::<FD_SET>() + ); + + // Don't create fd sets smaller than `FD_SET`. + assert_eq!( + fd_set_num_elements_for_fd_array(0) * size_of::<FdSetElement>(), + size_of::<FD_SET>() + ); + } + + #[test] + #[cfg(any(bsd, linux_kernel))] + fn layouts() { + use crate::backend::c; + + // The `FdSetElement` array should be suitably aligned. + assert_eq!(align_of::<FdSetElement>(), align_of::<c::fd_set>()); + + // The layout of `fd_set` should match our layout of a set of the same + // size. + assert_eq!( + fd_set_num_elements_for_bitvector(c::FD_SETSIZE as RawFd) * size_of::<FdSetElement>(), + size_of::<c::fd_set>() + ); + } +} |
