summaryrefslogtreecommitdiff
path: root/vendor/rustix/src/event
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/rustix/src/event')
-rw-r--r--vendor/rustix/src/event/epoll.rs347
-rw-r--r--vendor/rustix/src/event/eventfd.rs20
-rw-r--r--vendor/rustix/src/event/kqueue.rs466
-rw-r--r--vendor/rustix/src/event/mod.rs34
-rw-r--r--vendor/rustix/src/event/pause.rs31
-rw-r--r--vendor/rustix/src/event/poll.rs47
-rw-r--r--vendor/rustix/src/event/port.rs197
-rw-r--r--vendor/rustix/src/event/select.rs391
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&section=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&section=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&section=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&section=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&section=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>()
+ );
+ }
+}