summaryrefslogtreecommitdiffstats
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/eventfd.rs20
-rw-r--r--vendor/rustix/src/event/kqueue.rs431
-rw-r--r--vendor/rustix/src/event/mod.rs15
-rw-r--r--vendor/rustix/src/event/poll.rs32
-rw-r--r--vendor/rustix/src/event/port.rs150
5 files changed, 648 insertions, 0 deletions
diff --git a/vendor/rustix/src/event/eventfd.rs b/vendor/rustix/src/event/eventfd.rs
new file mode 100644
index 000000000..a76f2cfcf
--- /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 000000000..ff0ffddc0
--- /dev/null
+++ b/vendor/rustix/src/event/kqueue.rs
@@ -0,0 +1,431 @@
+//! An API for interfacing with `kqueue`.
+
+use crate::fd::{AsFd, OwnedFd, RawFd};
+use crate::pid::Pid;
+use crate::signal::Signal;
+use crate::{backend, io};
+
+use backend::c::{self, intptr_t, kevent as kevent_t, uintptr_t};
+use backend::event::syscalls;
+
+use alloc::vec::Vec;
+use core::mem::zeroed;
+use core::ptr::slice_from_raw_parts_mut;
+use core::time::Duration;
+
+/// A `kqueue` event.
+#[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: isize) -> 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 _, 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.
+ // TODO: Strict provenance, prevent int-to-ptr cast.
+ 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) -> isize {
+ // On netbsd, udata is an isize and not a pointer.
+ // TODO: Strict provenance, prevent ptr-to-int cast.
+
+ self.inner.udata 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 {
+ signal: Signal::from_raw(self.inner.ident as _).unwrap(),
+ 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 _;
+ }
+}
+
+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;
+ }
+}
+
+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;
+ }
+}
+
+#[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.
+ 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;
+ }
+}
+
+/// 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`.
+///
+/// Note: in order to receive events, make sure to allocate capacity in the
+/// eventlist! Otherwise, the function will return immediately.
+///
+/// # 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(
+ kqueue: impl AsFd,
+ changelist: &[Event],
+ eventlist: &mut Vec<Event>,
+ timeout: Option<Duration>,
+) -> io::Result<usize> {
+ let timeout = timeout.map(|timeout| backend::c::timespec {
+ tv_sec: timeout.as_secs() as _,
+ tv_nsec: timeout.subsec_nanos() as _,
+ });
+
+ // Populate the event list with events.
+ eventlist.set_len(0);
+ let out_slice = slice_from_raw_parts_mut(eventlist.as_mut_ptr().cast(), eventlist.capacity());
+ let res = syscalls::kevent(
+ kqueue.as_fd(),
+ changelist,
+ &mut *out_slice,
+ timeout.as_ref(),
+ )
+ .map(|res| res as _);
+
+ // Update the event list.
+ if let Ok(len) = res {
+ eventlist.set_len(len);
+ }
+
+ res
+}
diff --git a/vendor/rustix/src/event/mod.rs b/vendor/rustix/src/event/mod.rs
new file mode 100644
index 000000000..192afdcaa
--- /dev/null
+++ b/vendor/rustix/src/event/mod.rs
@@ -0,0 +1,15 @@
+//! Event operations.
+
+#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "illumos"))]
+mod eventfd;
+#[cfg(bsd)]
+pub mod kqueue;
+mod poll;
+#[cfg(solarish)]
+pub mod port;
+
+#[cfg(linux_kernel)]
+pub use crate::backend::event::epoll;
+#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "illumos"))]
+pub use eventfd::{eventfd, EventfdFlags};
+pub use poll::{poll, PollFd, PollFlags};
diff --git a/vendor/rustix/src/event/poll.rs b/vendor/rustix/src/event/poll.rs
new file mode 100644
index 000000000..2ee40521f
--- /dev/null
+++ b/vendor/rustix/src/event/poll.rs
@@ -0,0 +1,32 @@
+use crate::{backend, io};
+
+pub use backend::event::poll_fd::{PollFd, PollFlags};
+
+/// `poll(self.fds, timeout)`
+///
+/// # References
+/// - [Beej's Guide to Network Programming]
+/// - [POSIX]
+/// - [Linux]
+/// - [Apple]
+/// - [Winsock2]
+/// - [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/9699919799/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
+/// [Winsock2]: 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: i32) -> 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 000000000..b04f7c496
--- /dev/null
+++ b/vendor/rustix/src/event/port.rs
@@ -0,0 +1,150 @@
+//! Solaris/illumos event ports.
+
+use crate::backend::c;
+use crate::backend::event::syscalls;
+use crate::fd::{AsFd, AsRawFd, OwnedFd};
+use crate::io;
+
+use super::PollFlags;
+
+use core::time::Duration;
+
+/// The structure representing a port event.
+#[repr(transparent)]
+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 c::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
+pub fn port_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 `port_dissociate_fd`.
+///
+/// # References
+/// - [OpenSolaris]
+/// - [illumos]
+///
+/// [OpenSolaris]: https://www.unix.com/man-page/opensolaris/3C/port_associate/
+/// [illumos]: https://illumos.org/man/3C/port_associate
+pub unsafe fn port_associate_fd(
+ port: impl AsFd,
+ object: impl AsRawFd,
+ events: PollFlags,
+ userdata: *mut c::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 [`port_associate_fd`].
+///
+/// # References
+/// - [OpenSolaris]
+/// - [illumos]
+///
+/// [OpenSolaris]: https://www.unix.com/man-page/opensolaris/3C/port_dissociate
+/// [illumos]: https://illumos.org/man/3C/port_dissociate
+pub unsafe fn port_dissociate_fd(port: impl AsFd, object: impl AsRawFd) -> 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.
+///
+/// # References
+/// - [OpenSolaris]
+/// - [illumos]
+///
+/// [OpenSolaris]: https://www.unix.com/man-page/opensolaris/3C/port_get/
+/// [illumos]: https://illumos.org/man/3C/port_get
+pub fn port_get(port: impl AsFd, timeout: Option<Duration>) -> io::Result<Event> {
+ let mut timeout = timeout.map(|timeout| c::timespec {
+ tv_sec: timeout.as_secs().try_into().unwrap(),
+ tv_nsec: timeout.subsec_nanos() as _,
+ });
+
+ syscalls::port_get(port.as_fd(), timeout.as_mut())
+}
+
+/// `port_getn(port, events, min_events, timeout)`—Gets multiple events from a
+/// port.
+///
+/// # References
+/// - [OpenSolaris]
+/// - [illumos]
+///
+/// [OpenSolaris]: https://www.unix.com/man-page/opensolaris/3C/port_getn/
+/// [illumos]: https://illumos.org/man/3C/port_getn
+pub fn port_getn(
+ port: impl AsFd,
+ events: &mut Vec<Event>,
+ min_events: usize,
+ timeout: Option<Duration>,
+) -> io::Result<()> {
+ events.clear();
+
+ let mut timeout = timeout.map(|timeout| c::timespec {
+ tv_sec: timeout.as_secs().try_into().unwrap(),
+ tv_nsec: timeout.subsec_nanos() as _,
+ });
+
+ syscalls::port_getn(
+ port.as_fd(),
+ timeout.as_mut(),
+ events,
+ min_events.try_into().unwrap(),
+ )
+}
+
+/// `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
+pub fn port_send(port: impl AsFd, events: i32, userdata: *mut c::c_void) -> io::Result<()> {
+ syscalls::port_send(port.as_fd(), events, userdata.cast())
+}