summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlan Somers <asomers@gmail.com>2022-12-11 09:43:30 -0700
committerAlan Somers <asomers@gmail.com>2023-02-09 20:33:10 -0700
commit34f0eea7e3e4d6448efe2d9ffbb344f73d2e0fbe (patch)
tree5b7ad357d7682022d95c9fe5f75051b29f3d6fa1 /src
parentc42b6494d664463c03b2a96431bc500a53be5e49 (diff)
downloadnix-34f0eea7e3e4d6448efe2d9ffbb344f73d2e0fbe.zip
Rustier kqueue API
* Prefer methods instead of functions. * Create a newtype for a kqueue. * Document everything. * Deprecate EVFILT_SENDFILE, because it was never fully implemented upstream. * Add support to the libc_enum! macro to be able to deprecate variants.
Diffstat (limited to 'src')
-rw-r--r--src/macros.rs2
-rw-r--r--src/sys/event.rs213
-rw-r--r--src/sys/mod.rs1
3 files changed, 184 insertions, 32 deletions
diff --git a/src/macros.rs b/src/macros.rs
index 07d80f68..5d83a5ac 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -132,6 +132,8 @@ macro_rules! libc_enum {
impl ::std::convert::TryFrom<$repr> for $BitFlags {
type Error = $crate::Error;
#[allow(unused_doc_comments)]
+ #[allow(deprecated)]
+ #[allow(unused_attributes)]
fn try_from(x: $repr) -> $crate::Result<Self> {
match x {
$($try_froms)*
diff --git a/src/sys/event.rs b/src/sys/event.rs
index f21ba173..5dcf121a 100644
--- a/src/sys/event.rs
+++ b/src/sys/event.rs
@@ -1,5 +1,7 @@
-/* TOOD: Implement for other kqueue based systems
- */
+//! Kernel event notification mechanism
+//!
+//! # See Also
+//! [kqueue(2)](https://www.freebsd.org/cgi/man.cgi?query=kqueue)
use crate::{Errno, Result};
#[cfg(not(target_os = "netbsd"))]
@@ -8,16 +10,74 @@ use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t};
use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t};
use std::convert::TryInto;
use std::mem;
-use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd};
+use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd};
use std::ptr;
-// Redefine kevent in terms of programmer-friendly enums and bitfields.
+/// A kernel event queue. Used to notify a process of various asynchronous
+/// events.
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct KEvent {
kevent: libc::kevent,
}
+/// A kernel event queue.
+///
+/// Used by the kernel to notify the process of various types of asynchronous
+/// events.
+#[repr(transparent)]
+#[derive(Debug)]
+pub struct Kqueue(OwnedFd);
+
+impl Kqueue {
+ /// Create a new kernel event queue.
+ pub fn new() -> Result<Self> {
+ let res = unsafe { libc::kqueue() };
+
+ Errno::result(res).map(|fd| unsafe { Self(OwnedFd::from_raw_fd(fd)) })
+ }
+
+ /// Register new events with the kqueue, and return any pending events to
+ /// the user.
+ ///
+ /// This method will block until either the timeout expires, or a registered
+ /// event triggers a notification.
+ ///
+ /// # Arguments
+ /// - `changelist` - Any new kevents to register for notifications.
+ /// - `eventlist` - Storage space for the kernel to return notifications.
+ /// - `timeout` - An optional timeout.
+ ///
+ /// # Returns
+ /// Returns the number of events placed in the `eventlist`. If an error
+ /// occurs while processing an element of the `changelist` and there is
+ /// enough room in the `eventlist`, then the event will be placed in the
+ /// `eventlist` with `EV_ERROR` set in `flags` and the system error in
+ /// `data`.
+ pub fn kevent(
+ &self,
+ changelist: &[KEvent],
+ eventlist: &mut [KEvent],
+ timeout_opt: Option<timespec>,
+ ) -> Result<usize> {
+ let res = unsafe {
+ libc::kevent(
+ self.0.as_raw_fd(),
+ changelist.as_ptr() as *const libc::kevent,
+ changelist.len() as type_of_nchanges,
+ eventlist.as_mut_ptr() as *mut libc::kevent,
+ eventlist.len() as type_of_nchanges,
+ if let Some(ref timeout) = timeout_opt {
+ timeout as *const timespec
+ } else {
+ ptr::null()
+ }
+ )
+ };
+ Errno::result(res).map(|r| r as usize)
+ }
+}
+
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
@@ -37,22 +97,34 @@ libc_enum! {
#[cfg_attr(target_os = "netbsd", repr(u32))]
#[cfg_attr(not(target_os = "netbsd"), repr(i16))]
#[non_exhaustive]
+ /// Kqueue filter types. These are all the different types of event that a
+ /// kqueue can notify for.
pub enum EventFilter {
+ /// Notifies on the completion of a POSIX AIO operation.
EVFILT_AIO,
- /// Returns whenever there is no remaining data in the write buffer
#[cfg(target_os = "freebsd")]
+ /// Returns whenever there is no remaining data in the write buffer
EVFILT_EMPTY,
#[cfg(target_os = "dragonfly")]
+ /// Takes a descriptor as the identifier, and returns whenever one of
+ /// the specified exceptional conditions has occurred on the descriptor.
EVFILT_EXCEPT,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"))]
+ /// Establishes a file system monitor.
EVFILT_FS,
#[cfg(target_os = "freebsd")]
+ /// Notify for completion of a list of POSIX AIO operations.
+ /// # See Also
+ /// [lio_listio(2)](https://www.freebsd.org/cgi/man.cgi?query=lio_listio)
EVFILT_LIO,
#[cfg(any(target_os = "ios", target_os = "macos"))]
+ /// Mach portsets
EVFILT_MACHPORT,
+ /// Notifies when a process performs one or more of the requested
+ /// events.
EVFILT_PROC,
/// Returns events associated with the process referenced by a given
/// process descriptor, created by `pdfork()`. The events to monitor are:
@@ -60,20 +132,31 @@ libc_enum! {
/// - NOTE_EXIT: the process has exited. The exit status will be stored in data.
#[cfg(target_os = "freebsd")]
EVFILT_PROCDESC,
+ /// Takes a file descriptor as the identifier, and notifies whenever
+ /// there is data available to read.
EVFILT_READ,
- /// Returns whenever an asynchronous `sendfile()` call completes.
#[cfg(target_os = "freebsd")]
+ #[doc(hidden)]
+ #[deprecated(since = "0.27.0", note = "Never fully implemented by the OS")]
EVFILT_SENDFILE,
+ /// Takes a signal number to monitor as the identifier and notifies when
+ /// the given signal is delivered to the process.
EVFILT_SIGNAL,
+ /// Establishes a timer and notifies when the timer expires.
EVFILT_TIMER,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"))]
+ /// Notifies only when explicitly requested by the user.
EVFILT_USER,
#[cfg(any(target_os = "ios", target_os = "macos"))]
+ /// Virtual memory events
EVFILT_VM,
+ /// Notifies when a requested event happens on a specified file.
EVFILT_VNODE,
+ /// Takes a file descriptor as the identifier, and notifies whenever
+ /// it is possible to write to the file without blocking.
EVFILT_WRITE,
}
impl TryFrom<type_of_event_filter>
@@ -86,131 +169,194 @@ libc_enum! {
target_os = "macos",
target_os = "openbsd"
))]
+#[doc(hidden)]
pub type type_of_event_flag = u16;
#[cfg(any(target_os = "netbsd"))]
+#[doc(hidden)]
pub type type_of_event_flag = u32;
libc_bitflags! {
+ /// Event flags. See the man page for details.
+ // There's no useful documentation we can write for the individual flags
+ // that wouldn't simply be repeating the man page.
pub struct EventFlag: type_of_event_flag {
+ #[allow(missing_docs)]
EV_ADD;
+ #[allow(missing_docs)]
EV_CLEAR;
+ #[allow(missing_docs)]
EV_DELETE;
+ #[allow(missing_docs)]
EV_DISABLE;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
target_os = "ios", target_os = "macos",
target_os = "netbsd", target_os = "openbsd"))]
+ #[allow(missing_docs)]
EV_DISPATCH;
#[cfg(target_os = "freebsd")]
+ #[allow(missing_docs)]
EV_DROP;
+ #[allow(missing_docs)]
EV_ENABLE;
+ #[allow(missing_docs)]
EV_EOF;
+ #[allow(missing_docs)]
EV_ERROR;
#[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
EV_FLAG0;
+ #[allow(missing_docs)]
EV_FLAG1;
#[cfg(target_os = "dragonfly")]
+ #[allow(missing_docs)]
EV_NODATA;
+ #[allow(missing_docs)]
EV_ONESHOT;
#[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
EV_OOBAND;
#[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
EV_POLL;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
target_os = "ios", target_os = "macos",
target_os = "netbsd", target_os = "openbsd"))]
+ #[allow(missing_docs)]
EV_RECEIPT;
}
}
libc_bitflags!(
+ /// Filter-specific flags. See the man page for details.
+ // There's no useful documentation we can write for the individual flags
+ // that wouldn't simply be repeating the man page.
+ #[allow(missing_docs)]
pub struct FilterFlag: u32 {
#[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
NOTE_ABSOLUTE;
+ #[allow(missing_docs)]
NOTE_ATTRIB;
+ #[allow(missing_docs)]
NOTE_CHILD;
+ #[allow(missing_docs)]
NOTE_DELETE;
#[cfg(target_os = "openbsd")]
+ #[allow(missing_docs)]
NOTE_EOF;
+ #[allow(missing_docs)]
NOTE_EXEC;
+ #[allow(missing_docs)]
NOTE_EXIT;
#[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
NOTE_EXITSTATUS;
+ #[allow(missing_docs)]
NOTE_EXTEND;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
+ #[allow(missing_docs)]
NOTE_FFAND;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
+ #[allow(missing_docs)]
NOTE_FFCOPY;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
+ #[allow(missing_docs)]
NOTE_FFCTRLMASK;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
+ #[allow(missing_docs)]
NOTE_FFLAGSMASK;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
+ #[allow(missing_docs)]
NOTE_FFNOP;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
+ #[allow(missing_docs)]
NOTE_FFOR;
+ #[allow(missing_docs)]
NOTE_FORK;
+ #[allow(missing_docs)]
NOTE_LINK;
+ #[allow(missing_docs)]
NOTE_LOWAT;
#[cfg(target_os = "freebsd")]
+ #[allow(missing_docs)]
NOTE_MSECONDS;
#[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
NOTE_NONE;
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
+ #[allow(missing_docs)]
NOTE_NSECONDS;
#[cfg(target_os = "dragonfly")]
+ #[allow(missing_docs)]
NOTE_OOB;
+ #[allow(missing_docs)]
NOTE_PCTRLMASK;
+ #[allow(missing_docs)]
NOTE_PDATAMASK;
+ #[allow(missing_docs)]
NOTE_RENAME;
+ #[allow(missing_docs)]
NOTE_REVOKE;
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
+ #[allow(missing_docs)]
NOTE_SECONDS;
#[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
NOTE_SIGNAL;
+ #[allow(missing_docs)]
NOTE_TRACK;
+ #[allow(missing_docs)]
NOTE_TRACKERR;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
+ #[allow(missing_docs)]
NOTE_TRIGGER;
#[cfg(target_os = "openbsd")]
+ #[allow(missing_docs)]
NOTE_TRUNCATE;
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
+ #[allow(missing_docs)]
NOTE_USECONDS;
#[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
NOTE_VM_ERROR;
#[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
NOTE_VM_PRESSURE;
#[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
#[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
NOTE_VM_PRESSURE_TERMINATE;
+ #[allow(missing_docs)]
NOTE_WRITE;
}
);
-pub fn kqueue() -> Result<OwnedFd> {
- let res = unsafe { libc::kqueue() };
-
- Errno::result(res).map(|fd| unsafe { OwnedFd::from_raw_fd(fd) })
+#[allow(missing_docs)]
+#[deprecated(since = "0.27.0", note = "Use KEvent::new instead")]
+pub fn kqueue() -> Result<Kqueue> {
+ Kqueue::new()
}
// KEvent can't derive Send because on some operating systems, udata is defined
@@ -220,6 +366,8 @@ unsafe impl Send for KEvent {}
impl KEvent {
#[allow(clippy::needless_update)] // Not needless on all platforms.
+ /// Construct a new `KEvent` suitable for submission to the kernel via the
+ /// `changelist` argument of [`Kqueue::kevent`].
pub fn new(
ident: uintptr_t,
filter: EventFilter,
@@ -242,33 +390,46 @@ impl KEvent {
}
}
+ /// Value used to identify this event. The exact interpretation is
+ /// determined by the attached filter, but often is a raw file descriptor.
pub fn ident(&self) -> uintptr_t {
self.kevent.ident
}
+ /// Identifies the kernel filter used to process this event.
+ ///
+ /// Will only return an error if the kernel reports an event via a filter
+ /// that is unknown to Nix.
pub fn filter(&self) -> Result<EventFilter> {
self.kevent.filter.try_into()
}
+ /// Flags control what the kernel will do when this event is added with
+ /// [`Kqueue::kevent`].
pub fn flags(&self) -> EventFlag {
EventFlag::from_bits(self.kevent.flags).unwrap()
}
+ /// Filter-specific flags.
pub fn fflags(&self) -> FilterFlag {
FilterFlag::from_bits(self.kevent.fflags).unwrap()
}
+ /// Filter-specific data value.
pub fn data(&self) -> intptr_t {
self.kevent.data as intptr_t
}
+ /// Opaque user-defined value passed through the kernel unchanged.
pub fn udata(&self) -> intptr_t {
self.kevent.udata as intptr_t
}
}
-pub fn kevent<Fd: AsFd>(
- kq: Fd,
+#[allow(missing_docs)]
+#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
+pub fn kevent(
+ kq: &Kqueue,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_ms: usize,
@@ -279,7 +440,7 @@ pub fn kevent<Fd: AsFd>(
tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long,
};
- kevent_ts(kq, changelist, eventlist, Some(timeout))
+ kq.kevent(changelist, eventlist, Some(timeout))
}
#[cfg(any(
@@ -293,30 +454,20 @@ type type_of_nchanges = c_int;
#[cfg(target_os = "netbsd")]
type type_of_nchanges = size_t;
-pub fn kevent_ts<Fd: AsFd>(
- kq: Fd,
+#[allow(missing_docs)]
+#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
+pub fn kevent_ts(
+ kq: &Kqueue,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_opt: Option<timespec>,
) -> Result<usize> {
- let res = unsafe {
- libc::kevent(
- kq.as_fd().as_raw_fd(),
- changelist.as_ptr() as *const libc::kevent,
- changelist.len() as type_of_nchanges,
- eventlist.as_mut_ptr() as *mut libc::kevent,
- eventlist.len() as type_of_nchanges,
- if let Some(ref timeout) = timeout_opt {
- timeout as *const timespec
- } else {
- ptr::null()
- },
- )
- };
-
- Errno::result(res).map(|r| r as usize)
+ kq.kevent(changelist, eventlist, timeout_opt)
}
+/// Modify an existing [`KEvent`].
+// Probably should deprecate. Would anybody ever use it over `KEvent::new`?
+#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
#[inline]
pub fn ev_set(
ev: &mut KEvent,
diff --git a/src/sys/mod.rs b/src/sys/mod.rs
index 2065059d..383f08df 100644
--- a/src/sys/mod.rs
+++ b/src/sys/mod.rs
@@ -25,7 +25,6 @@ feature! {
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
- #[allow(missing_docs)]
pub mod event;
#[cfg(any(target_os = "android", target_os = "linux"))]