From fea85d3726f8b6c964bb2acc8af16ef3f64ed9ac Mon Sep 17 00:00:00 2001 From: jtnunley Date: Wed, 1 Feb 2023 09:45:55 -0800 Subject: [PATCH 1/9] feat: Expose other kqueue filters --- examples/wait-signal.rs | 80 ++++++++++ src/kqueue.rs | 26 ++- src/lib.rs | 2 + src/os.rs | 343 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 444 insertions(+), 7 deletions(-) create mode 100644 examples/wait-signal.rs create mode 100644 src/os.rs diff --git a/examples/wait-signal.rs b/examples/wait-signal.rs new file mode 100644 index 0000000..eb1eb65 --- /dev/null +++ b/examples/wait-signal.rs @@ -0,0 +1,80 @@ +#[cfg(all( + any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +))] +mod example { + use polling::os::kqueue::{PollerKqueueExt, Signal}; + use polling::{PollMode, Poller}; + + pub(super) fn main2() { + // Create a poller. + let poller = Poller::new().unwrap(); + + // Register SIGINT in the poller. + let sigint = Signal::new(libc::SIGINT); + poller.add_filter(sigint, 1, PollMode::Oneshot).unwrap(); + + let mut events = vec![]; + + println!("Press Ctrl+C to exit..."); + + loop { + // Wait for events. + poller.wait(&mut events, None).unwrap(); + + // Process events. + for ev in events.drain(..) { + match ev.key { + 1 => { + println!("SIGINT received"); + return; + } + _ => unreachable!(), + } + } + } + } +} + +#[cfg(all( + any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +))] +fn main() { + example::main2(); +} + +#[cfg(not(all( + any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +)))] +fn main() { + eprintln!("This example is only supported on kqueue-based platforms."); +} diff --git a/src/kqueue.rs b/src/kqueue.rs index 7ace814..80071bf 100644 --- a/src/kqueue.rs +++ b/src/kqueue.rs @@ -65,11 +65,7 @@ impl Poller { log::trace!("add: kqueue_fd={}, fd={}, ev={:?}", self.kqueue_fd, fd, ev); } - let mode_flags = match mode { - PollMode::Oneshot => libc::EV_ONESHOT, - PollMode::Level => 0, - PollMode::Edge => libc::EV_CLEAR, - }; + let mode_flags = mode_to_flags(mode); let read_flags = if ev.readable { libc::EV_ADD | mode_flags @@ -105,7 +101,7 @@ impl Poller { } /// Submit one or more changes to the kernel queue and check to see if they succeeded. - fn submit_changes(&self, changelist: A) -> io::Result<()> + pub(crate) fn submit_changes(&self, changelist: A) -> io::Result<()> where A: Copy + AsRef<[libc::kevent]> + AsMut<[libc::kevent]>, { @@ -229,19 +225,35 @@ impl Events { /// Iterates over I/O events. pub fn iter(&self) -> impl Iterator + '_ { + const READABLES: &[libc::c_short] = &[ + libc::EVFILT_READ, + libc::EVFILT_VNODE, + libc::EVFILT_PROC, + libc::EVFILT_SIGNAL, + libc::EVFILT_TIMER, + ]; + // On some platforms, closing the read end of a pipe wakes up writers, but the // event is reported as EVFILT_READ with the EV_EOF flag. // // https://github.com/golang/go/commit/23aad448b1e3f7c3b4ba2af90120bde91ac865b4 self.list[..self.len].iter().map(|ev| Event { key: ev.udata as usize, - readable: ev.filter == libc::EVFILT_READ, + readable: READABLES.contains(&ev.filter), writable: ev.filter == libc::EVFILT_WRITE || (ev.filter == libc::EVFILT_READ && (ev.flags & libc::EV_EOF) != 0), }) } } +pub(crate) fn mode_to_flags(mode: PollMode) -> libc::c_ushort { + match mode { + PollMode::Oneshot => libc::EV_ONESHOT, + PollMode::Level => 0, + PollMode::Edge => libc::EV_CLEAR, + } +} + #[cfg(any( target_os = "freebsd", target_os = "dragonfly", diff --git a/src/lib.rs b/src/lib.rs index d388283..a6795c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,6 +120,8 @@ cfg_if! { } } +pub mod os; + /// Key associated with notifications. const NOTIFY_KEY: usize = std::usize::MAX; diff --git a/src/os.rs b/src/os.rs new file mode 100644 index 0000000..c7d3360 --- /dev/null +++ b/src/os.rs @@ -0,0 +1,343 @@ +//! Platform-specific functionality. + +#[cfg(all( + any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +))] +/// Functionality that is only available for `kqueue`-based platforms. +pub mod kqueue { + use crate::sys::mode_to_flags; + use crate::{PollMode, Poller}; + use __private::{FilterSealed, PollerSealed}; + use std::convert::TryInto; + use std::os::unix::prelude::*; + use std::process::Child; + use std::time::Duration; + use std::{io, mem}; + + /// Functionality that is only available for `kqueue`-based platforms. + /// + /// `kqueue` is able to monitor much more than just read/write readiness on file descriptors. Using + /// this extension trait, you can monitor for signals, process exits, and more. See the implementors + /// of the [`Filter`] trait for more information. + pub trait PollerKqueueExt: PollerSealed { + /// Add a filter to the poller. + /// + /// This is similar to [`add`][Poller::add], but it allows you to specify a filter instead of + /// a socket. See the implementors of the [`Filter`] trait for more information. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, PollMode}; + /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; + /// + /// let poller = Poller::new().unwrap(); + /// + /// // Register the SIGINT signal. + /// poller.add_filter(Signal::new(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); + /// + /// // Wait for the signal. + /// let mut events = vec![]; + /// poller.wait(&mut events, None).unwrap(); + /// # let _ = events; + /// ``` + fn add_filter(&self, filter: impl Filter, key: usize, mode: PollMode) -> io::Result<()>; + + /// Modify a filter in the poller. + /// + /// This is similar to [`modify`][Poller::modify], but it allows you to specify a filter + /// instead of a socket. See the implementors of the [`Filter`] trait for more information. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, PollMode}; + /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; + /// + /// let poller = Poller::new().unwrap(); + /// + /// // Register the SIGINT signal. + /// poller.add_filter(Signal::new(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); + /// + /// // Re-register with a different key. + /// poller.modify_filter(Signal::new(libc::SIGINT), 1, PollMode::Oneshot).unwrap(); + /// + /// // Wait for the signal. + /// let mut events = vec![]; + /// poller.wait(&mut events, None).unwrap(); + /// # let _ = events; + /// ``` + fn modify_filter(&self, filter: impl Filter, key: usize, mode: PollMode) -> io::Result<()>; + + /// Remove a filter from the poller. + /// + /// This is used to remove filters that were previously added with + /// [`add_filter`]. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, PollMode}; + /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; + /// + /// let poller = Poller::new().unwrap(); + /// + /// // Register the SIGINT signal. + /// poller.add_filter(Signal::new(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); + /// + /// // Remove the filter. + /// poller.delete_filter(Signal::new(libc::SIGINT)).unwrap(); + /// ``` + fn delete_filter(&self, filter: impl Filter) -> io::Result<()>; + } + + impl PollerSealed for Poller {} + + impl PollerKqueueExt for Poller { + fn add_filter(&self, filter: impl Filter, key: usize, mode: PollMode) -> io::Result<()> { + // No difference between adding and modifying in kqueue. + self.modify_filter(filter, key, mode) + } + + fn modify_filter(&self, filter: impl Filter, key: usize, mode: PollMode) -> io::Result<()> { + // Convert the filter into a kevent. + let event = filter.filter(libc::EV_ADD | mode_to_flags(mode), key); + + // Modify the filter. + self.poller.submit_changes([event]) + } + + fn delete_filter(&self, filter: impl Filter) -> io::Result<()> { + // Convert the filter into a kevent. + let event = filter.filter(libc::EV_DELETE, 0); + + // Delete the filter. + self.poller.submit_changes([event]) + } + } + + /// A filter that can be registered into a `kqueue`. + pub trait Filter: FilterSealed {} + + unsafe impl FilterSealed for &T { + fn filter(&self, flags: u16, key: usize) -> libc::kevent { + (**self).filter(flags, key) + } + } + + impl Filter for &T {} + + /// Monitor this signal number. + /// + /// No matter what `PollMode` is specified, this filter will always be + /// oneshot-only. + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct Signal(c_int); + + impl Signal { + /// Monitor this signal number. + pub fn new(signal: c_int) -> Self { + Self(signal) + } + + /// Get the signal number. + pub fn signal(self) -> c_int { + self.0 + } + } + + /// Alias for `libc::c_int`. + #[allow(non_camel_case_types)] + pub type c_int = i32; + + unsafe impl FilterSealed for Signal { + fn filter(&self, flags: u16, key: usize) -> libc::kevent { + libc::kevent { + ident: self.0 as _, + filter: libc::EVFILT_SIGNAL, + flags: flags | libc::EV_RECEIPT, + udata: key as _, + ..unsafe { mem::zeroed() } + } + } + } + + impl Filter for Signal {} + + /// Monitor a child process. + #[derive(Debug)] + pub struct Process<'a> { + /// The child process to monitor. + child: &'a Child, + + /// The operation to monitor. + ops: ProcessOps, + } + + /// The operations that a monitored process can perform. + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[non_exhaustive] + pub enum ProcessOps { + /// The process exited. + Exit, + + /// The process was forked. + Fork, + + /// The process executed a new process. + Exec, + } + + impl<'a> Process<'a> { + /// Monitor a child process. + pub fn new(child: &'a Child, ops: ProcessOps) -> Self { + Self { child, ops } + } + } + + unsafe impl FilterSealed for Process<'_> { + fn filter(&self, flags: u16, key: usize) -> libc::kevent { + let fflags = match self.ops { + ProcessOps::Exit => libc::NOTE_EXIT, + ProcessOps::Fork => libc::NOTE_FORK, + ProcessOps::Exec => libc::NOTE_EXEC, + }; + + libc::kevent { + ident: self.child.id() as _, + filter: libc::EVFILT_PROC, + flags: flags | libc::EV_RECEIPT, + fflags, + udata: key as _, + ..unsafe { mem::zeroed() } + } + } + } + + impl Filter for Process<'_> {} + + /// Monitor a file on disk. + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct Vnode(RawFd); + + impl Vnode { + /// Monitor a file on disk. + pub fn new(fd: RawFd) -> Self { + Self(fd) + } + + /// Get the file descriptor. + pub fn fd(self) -> RawFd { + self.0 + } + } + + unsafe impl FilterSealed for Vnode { + fn filter(&self, flags: u16, key: usize) -> libc::kevent { + libc::kevent { + ident: self.0 as _, + filter: libc::EVFILT_VNODE, + flags: flags | libc::EV_RECEIPT, + udata: key as _, + ..unsafe { mem::zeroed() } + } + } + } + + impl Filter for Vnode {} + + /// Wait for a timeout to expire. + /// + /// Modifying the timeout after it has been added to the poller will reset it. + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct Timer { + /// Identifier for the timer. + id: usize, + + /// The timeout to wait for. + timeout: Duration, + } + + impl Timer { + /// Create a new timer. + pub fn new(id: usize, timeout: Duration) -> Self { + Self { id, timeout } + } + + /// Get the identifier for the timer. + pub fn id(self) -> usize { + self.id + } + + /// Get the timeout to wait for. + pub fn timeout(self) -> Duration { + self.timeout + } + } + + unsafe impl FilterSealed for Timer { + fn filter(&self, flags: u16, key: usize) -> libc::kevent { + // Figure out the granularity of the timer. + let (fflags, data) = { + let subsec_nanos = self.timeout.subsec_nanos(); + + match (subsec_nanos % 1_000, subsec_nanos) { + (_, 0) => ( + libc::NOTE_SECONDS, + self.timeout.as_secs().try_into().expect("too many seconds"), + ), + (0, _) => ( + libc::NOTE_USECONDS, + self.timeout + .as_micros() + .try_into() + .expect("too many microseconds"), + ), + (_, _) => ( + libc::NOTE_NSECONDS, + self.timeout + .as_nanos() + .try_into() + .expect("too many nanoseconds"), + ), + } + }; + + #[allow(clippy::needless_update)] + libc::kevent { + ident: self.id as _, + filter: libc::EVFILT_TIMER, + flags: flags | libc::EV_RECEIPT, + fflags, + data, + udata: key as _, + ..unsafe { mem::zeroed() } + } + } + } + + impl Filter for Timer {} + + mod __private { + #[doc(hidden)] + pub trait PollerSealed {} + + #[doc(hidden)] + pub unsafe trait FilterSealed { + /// Get the filter for the given event. + /// + /// This filter's flags must have `EV_RECEIPT`. + fn filter(&self, flags: u16, key: usize) -> libc::kevent; + } + } +} From 324b48022fe3fe54dd3665bf5e6571d1ec33bb1f Mon Sep 17 00:00:00 2001 From: jtnunley Date: Wed, 1 Feb 2023 10:25:34 -0800 Subject: [PATCH 2/9] Fix netbsd/openbsd compilation --- src/kqueue.rs | 16 ++++++++-- src/os.rs | 86 ++++++++++++++++++++++++++++++++++----------------- 2 files changed, 71 insertions(+), 31 deletions(-) diff --git a/src/kqueue.rs b/src/kqueue.rs index 80071bf..bc127d0 100644 --- a/src/kqueue.rs +++ b/src/kqueue.rs @@ -225,7 +225,7 @@ impl Events { /// Iterates over I/O events. pub fn iter(&self) -> impl Iterator + '_ { - const READABLES: &[libc::c_short] = &[ + const READABLES: &[FilterName] = &[ libc::EVFILT_READ, libc::EVFILT_VNODE, libc::EVFILT_PROC, @@ -246,7 +246,7 @@ impl Events { } } -pub(crate) fn mode_to_flags(mode: PollMode) -> libc::c_ushort { +pub(crate) fn mode_to_flags(mode: PollMode) -> FilterFlags { match mode { PollMode::Oneshot => libc::EV_ONESHOT, PollMode::Level => 0, @@ -254,6 +254,18 @@ pub(crate) fn mode_to_flags(mode: PollMode) -> libc::c_ushort { } } +#[cfg(target_os = "netbsd")] +pub(crate) type FilterFlags = u32; + +#[cfg(not(target_os = "netbsd"))] +pub(crate) type FilterFlags = libc::c_ushort; + +#[cfg(target_os = "netbsd")] +pub(crate) type FilterName = u32; + +#[cfg(not(target_os = "netbsd"))] +pub(crate) type FilterName = libc::c_short; + #[cfg(any( target_os = "freebsd", target_os = "dragonfly", diff --git a/src/os.rs b/src/os.rs index c7d3360..44c9c9d 100644 --- a/src/os.rs +++ b/src/os.rs @@ -15,15 +15,18 @@ ))] /// Functionality that is only available for `kqueue`-based platforms. pub mod kqueue { - use crate::sys::mode_to_flags; + use crate::sys::{mode_to_flags, FilterFlags}; use crate::{PollMode, Poller}; use __private::{FilterSealed, PollerSealed}; - use std::convert::TryInto; use std::os::unix::prelude::*; use std::process::Child; - use std::time::Duration; use std::{io, mem}; + #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] + use std::convert::TryInto; + #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] + use std::time::Duration; + /// Functionality that is only available for `kqueue`-based platforms. /// /// `kqueue` is able to monitor much more than just read/write readiness on file descriptors. Using @@ -82,7 +85,7 @@ pub mod kqueue { /// Remove a filter from the poller. /// /// This is used to remove filters that were previously added with - /// [`add_filter`]. + /// [`add_filter`](PollerKqueueExt::add_filter). /// /// # Examples /// @@ -130,7 +133,7 @@ pub mod kqueue { pub trait Filter: FilterSealed {} unsafe impl FilterSealed for &T { - fn filter(&self, flags: u16, key: usize) -> libc::kevent { + fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { (**self).filter(flags, key) } } @@ -161,7 +164,7 @@ pub mod kqueue { pub type c_int = i32; unsafe impl FilterSealed for Signal { - fn filter(&self, flags: u16, key: usize) -> libc::kevent { + fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { libc::kevent { ident: self.0 as _, filter: libc::EVFILT_SIGNAL, @@ -206,7 +209,7 @@ pub mod kqueue { } unsafe impl FilterSealed for Process<'_> { - fn filter(&self, flags: u16, key: usize) -> libc::kevent { + fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { let fflags = match self.ops { ProcessOps::Exit => libc::NOTE_EXIT, ProcessOps::Fork => libc::NOTE_FORK, @@ -243,7 +246,7 @@ pub mod kqueue { } unsafe impl FilterSealed for Vnode { - fn filter(&self, flags: u16, key: usize) -> libc::kevent { + fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { libc::kevent { ident: self.0 as _, filter: libc::EVFILT_VNODE, @@ -259,6 +262,11 @@ pub mod kqueue { /// Wait for a timeout to expire. /// /// Modifying the timeout after it has been added to the poller will reset it. + #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] + #[cfg_attr( + docsrs, + doc(cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))) + )] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Timer { /// Identifier for the timer. @@ -268,6 +276,7 @@ pub mod kqueue { timeout: Duration, } + #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] impl Timer { /// Create a new timer. pub fn new(id: usize, timeout: Duration) -> Self { @@ -285,31 +294,47 @@ pub mod kqueue { } } + #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] unsafe impl FilterSealed for Timer { - fn filter(&self, flags: u16, key: usize) -> libc::kevent { + fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { // Figure out the granularity of the timer. let (fflags, data) = { - let subsec_nanos = self.timeout.subsec_nanos(); - - match (subsec_nanos % 1_000, subsec_nanos) { - (_, 0) => ( - libc::NOTE_SECONDS, - self.timeout.as_secs().try_into().expect("too many seconds"), - ), - (0, _) => ( - libc::NOTE_USECONDS, - self.timeout - .as_micros() - .try_into() - .expect("too many microseconds"), - ), - (_, _) => ( - libc::NOTE_NSECONDS, + #[cfg(not(target_os = "openbsd"))] + { + let subsec_nanos = self.timeout.subsec_nanos(); + + match (subsec_nanos % 1_000, subsec_nanos) { + (_, 0) => ( + libc::NOTE_SECONDS, + self.timeout.as_secs().try_into().expect("too many seconds"), + ), + (0, _) => ( + libc::NOTE_USECONDS, + self.timeout + .as_micros() + .try_into() + .expect("too many microseconds"), + ), + (_, _) => ( + libc::NOTE_NSECONDS, + self.timeout + .as_nanos() + .try_into() + .expect("too many nanoseconds"), + ), + } + } + + #[cfg(target_os = "openbsd")] + { + // OpenBSD only supports milliseconds. + ( + 0, self.timeout - .as_nanos() + .as_millis() .try_into() - .expect("too many nanoseconds"), - ), + .expect("too many milliseconds"), + ) } }; @@ -326,9 +351,12 @@ pub mod kqueue { } } + #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] impl Filter for Timer {} mod __private { + use crate::sys::FilterFlags; + #[doc(hidden)] pub trait PollerSealed {} @@ -337,7 +365,7 @@ pub mod kqueue { /// Get the filter for the given event. /// /// This filter's flags must have `EV_RECEIPT`. - fn filter(&self, flags: u16, key: usize) -> libc::kevent; + fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent; } } } From 1b18e14fbc202b01c1df0fb62f52a4fb76ae3a10 Mon Sep 17 00:00:00 2001 From: jtnunley Date: Wed, 1 Feb 2023 10:39:24 -0800 Subject: [PATCH 3/9] Build MSRV for FreeBsd/OpenBsd in CI --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5cefdea..1bb822e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -169,6 +169,11 @@ jobs: # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} - run: cargo build + - name: Install Other Targets + if: startsWith(matrix.os, 'ubuntu') + run: rustup target add x86_64-unknown-freebsd x86_64-unknown-netbsd + - run: cargo build --target x86_64-unknown-freebsd + - run: cargo build --target x86_64-unknown-netbsd clippy: runs-on: ubuntu-latest From 6d8fa234db9a794e297a6ac74bdc57879c554cd1 Mon Sep 17 00:00:00 2001 From: jtnunley Date: Wed, 1 Feb 2023 10:41:32 -0800 Subject: [PATCH 4/9] Only run MSRV BSD builds on Linux --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1bb822e..055170c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -173,7 +173,9 @@ jobs: if: startsWith(matrix.os, 'ubuntu') run: rustup target add x86_64-unknown-freebsd x86_64-unknown-netbsd - run: cargo build --target x86_64-unknown-freebsd + if: startsWith(matrix.os, 'ubuntu') - run: cargo build --target x86_64-unknown-netbsd + if: startsWith(matrix.os, 'ubuntu') clippy: runs-on: ubuntu-latest From 7497fcfbc8593d69ef5b7d853d92ac81d32d1743 Mon Sep 17 00:00:00 2001 From: jtnunley Date: Fri, 3 Feb 2023 09:55:23 -0800 Subject: [PATCH 5/9] Change API a little + fix netbsd timer --- src/os.rs | 357 +---------------------------------------------- src/os/kqueue.rs | 329 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 330 insertions(+), 356 deletions(-) create mode 100644 src/os/kqueue.rs diff --git a/src/os.rs b/src/os.rs index 44c9c9d..1e68e46 100644 --- a/src/os.rs +++ b/src/os.rs @@ -13,359 +13,4 @@ ), not(polling_test_poll_backend), ))] -/// Functionality that is only available for `kqueue`-based platforms. -pub mod kqueue { - use crate::sys::{mode_to_flags, FilterFlags}; - use crate::{PollMode, Poller}; - use __private::{FilterSealed, PollerSealed}; - use std::os::unix::prelude::*; - use std::process::Child; - use std::{io, mem}; - - #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] - use std::convert::TryInto; - #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] - use std::time::Duration; - - /// Functionality that is only available for `kqueue`-based platforms. - /// - /// `kqueue` is able to monitor much more than just read/write readiness on file descriptors. Using - /// this extension trait, you can monitor for signals, process exits, and more. See the implementors - /// of the [`Filter`] trait for more information. - pub trait PollerKqueueExt: PollerSealed { - /// Add a filter to the poller. - /// - /// This is similar to [`add`][Poller::add], but it allows you to specify a filter instead of - /// a socket. See the implementors of the [`Filter`] trait for more information. - /// - /// # Examples - /// - /// ```no_run - /// use polling::{Poller, PollMode}; - /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; - /// - /// let poller = Poller::new().unwrap(); - /// - /// // Register the SIGINT signal. - /// poller.add_filter(Signal::new(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); - /// - /// // Wait for the signal. - /// let mut events = vec![]; - /// poller.wait(&mut events, None).unwrap(); - /// # let _ = events; - /// ``` - fn add_filter(&self, filter: impl Filter, key: usize, mode: PollMode) -> io::Result<()>; - - /// Modify a filter in the poller. - /// - /// This is similar to [`modify`][Poller::modify], but it allows you to specify a filter - /// instead of a socket. See the implementors of the [`Filter`] trait for more information. - /// - /// # Examples - /// - /// ```no_run - /// use polling::{Poller, PollMode}; - /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; - /// - /// let poller = Poller::new().unwrap(); - /// - /// // Register the SIGINT signal. - /// poller.add_filter(Signal::new(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); - /// - /// // Re-register with a different key. - /// poller.modify_filter(Signal::new(libc::SIGINT), 1, PollMode::Oneshot).unwrap(); - /// - /// // Wait for the signal. - /// let mut events = vec![]; - /// poller.wait(&mut events, None).unwrap(); - /// # let _ = events; - /// ``` - fn modify_filter(&self, filter: impl Filter, key: usize, mode: PollMode) -> io::Result<()>; - - /// Remove a filter from the poller. - /// - /// This is used to remove filters that were previously added with - /// [`add_filter`](PollerKqueueExt::add_filter). - /// - /// # Examples - /// - /// ```no_run - /// use polling::{Poller, PollMode}; - /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; - /// - /// let poller = Poller::new().unwrap(); - /// - /// // Register the SIGINT signal. - /// poller.add_filter(Signal::new(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); - /// - /// // Remove the filter. - /// poller.delete_filter(Signal::new(libc::SIGINT)).unwrap(); - /// ``` - fn delete_filter(&self, filter: impl Filter) -> io::Result<()>; - } - - impl PollerSealed for Poller {} - - impl PollerKqueueExt for Poller { - fn add_filter(&self, filter: impl Filter, key: usize, mode: PollMode) -> io::Result<()> { - // No difference between adding and modifying in kqueue. - self.modify_filter(filter, key, mode) - } - - fn modify_filter(&self, filter: impl Filter, key: usize, mode: PollMode) -> io::Result<()> { - // Convert the filter into a kevent. - let event = filter.filter(libc::EV_ADD | mode_to_flags(mode), key); - - // Modify the filter. - self.poller.submit_changes([event]) - } - - fn delete_filter(&self, filter: impl Filter) -> io::Result<()> { - // Convert the filter into a kevent. - let event = filter.filter(libc::EV_DELETE, 0); - - // Delete the filter. - self.poller.submit_changes([event]) - } - } - - /// A filter that can be registered into a `kqueue`. - pub trait Filter: FilterSealed {} - - unsafe impl FilterSealed for &T { - fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { - (**self).filter(flags, key) - } - } - - impl Filter for &T {} - - /// Monitor this signal number. - /// - /// No matter what `PollMode` is specified, this filter will always be - /// oneshot-only. - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct Signal(c_int); - - impl Signal { - /// Monitor this signal number. - pub fn new(signal: c_int) -> Self { - Self(signal) - } - - /// Get the signal number. - pub fn signal(self) -> c_int { - self.0 - } - } - - /// Alias for `libc::c_int`. - #[allow(non_camel_case_types)] - pub type c_int = i32; - - unsafe impl FilterSealed for Signal { - fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { - libc::kevent { - ident: self.0 as _, - filter: libc::EVFILT_SIGNAL, - flags: flags | libc::EV_RECEIPT, - udata: key as _, - ..unsafe { mem::zeroed() } - } - } - } - - impl Filter for Signal {} - - /// Monitor a child process. - #[derive(Debug)] - pub struct Process<'a> { - /// The child process to monitor. - child: &'a Child, - - /// The operation to monitor. - ops: ProcessOps, - } - - /// The operations that a monitored process can perform. - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - #[non_exhaustive] - pub enum ProcessOps { - /// The process exited. - Exit, - - /// The process was forked. - Fork, - - /// The process executed a new process. - Exec, - } - - impl<'a> Process<'a> { - /// Monitor a child process. - pub fn new(child: &'a Child, ops: ProcessOps) -> Self { - Self { child, ops } - } - } - - unsafe impl FilterSealed for Process<'_> { - fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { - let fflags = match self.ops { - ProcessOps::Exit => libc::NOTE_EXIT, - ProcessOps::Fork => libc::NOTE_FORK, - ProcessOps::Exec => libc::NOTE_EXEC, - }; - - libc::kevent { - ident: self.child.id() as _, - filter: libc::EVFILT_PROC, - flags: flags | libc::EV_RECEIPT, - fflags, - udata: key as _, - ..unsafe { mem::zeroed() } - } - } - } - - impl Filter for Process<'_> {} - - /// Monitor a file on disk. - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct Vnode(RawFd); - - impl Vnode { - /// Monitor a file on disk. - pub fn new(fd: RawFd) -> Self { - Self(fd) - } - - /// Get the file descriptor. - pub fn fd(self) -> RawFd { - self.0 - } - } - - unsafe impl FilterSealed for Vnode { - fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { - libc::kevent { - ident: self.0 as _, - filter: libc::EVFILT_VNODE, - flags: flags | libc::EV_RECEIPT, - udata: key as _, - ..unsafe { mem::zeroed() } - } - } - } - - impl Filter for Vnode {} - - /// Wait for a timeout to expire. - /// - /// Modifying the timeout after it has been added to the poller will reset it. - #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] - #[cfg_attr( - docsrs, - doc(cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))) - )] - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct Timer { - /// Identifier for the timer. - id: usize, - - /// The timeout to wait for. - timeout: Duration, - } - - #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] - impl Timer { - /// Create a new timer. - pub fn new(id: usize, timeout: Duration) -> Self { - Self { id, timeout } - } - - /// Get the identifier for the timer. - pub fn id(self) -> usize { - self.id - } - - /// Get the timeout to wait for. - pub fn timeout(self) -> Duration { - self.timeout - } - } - - #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] - unsafe impl FilterSealed for Timer { - fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { - // Figure out the granularity of the timer. - let (fflags, data) = { - #[cfg(not(target_os = "openbsd"))] - { - let subsec_nanos = self.timeout.subsec_nanos(); - - match (subsec_nanos % 1_000, subsec_nanos) { - (_, 0) => ( - libc::NOTE_SECONDS, - self.timeout.as_secs().try_into().expect("too many seconds"), - ), - (0, _) => ( - libc::NOTE_USECONDS, - self.timeout - .as_micros() - .try_into() - .expect("too many microseconds"), - ), - (_, _) => ( - libc::NOTE_NSECONDS, - self.timeout - .as_nanos() - .try_into() - .expect("too many nanoseconds"), - ), - } - } - - #[cfg(target_os = "openbsd")] - { - // OpenBSD only supports milliseconds. - ( - 0, - self.timeout - .as_millis() - .try_into() - .expect("too many milliseconds"), - ) - } - }; - - #[allow(clippy::needless_update)] - libc::kevent { - ident: self.id as _, - filter: libc::EVFILT_TIMER, - flags: flags | libc::EV_RECEIPT, - fflags, - data, - udata: key as _, - ..unsafe { mem::zeroed() } - } - } - } - - #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] - impl Filter for Timer {} - - mod __private { - use crate::sys::FilterFlags; - - #[doc(hidden)] - pub trait PollerSealed {} - - #[doc(hidden)] - pub unsafe trait FilterSealed { - /// Get the filter for the given event. - /// - /// This filter's flags must have `EV_RECEIPT`. - fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent; - } - } -} +pub mod kqueue; diff --git a/src/os/kqueue.rs b/src/os/kqueue.rs new file mode 100644 index 0000000..cd3617c --- /dev/null +++ b/src/os/kqueue.rs @@ -0,0 +1,329 @@ +//! Functionality that is only available for `kqueue`-based platforms. + +use crate::sys::{mode_to_flags, FilterFlags}; +use crate::{PollMode, Poller}; +use __private::{FilterSealed, PollerSealed}; +use std::convert::TryInto; +use std::process::Child; +use std::time::Duration; +use std::{io, mem}; + +// TODO(notgull): We should also have EVFILT_AIO, EVFILT_VNODE and EVFILT_USER. However, the current +// API makes it difficult to effectively express events from these filters. At the next breaking +// change, we should change `Event` to be a struct with private fields, and encode additional +// information in there. + +/// Functionality that is only available for `kqueue`-based platforms. +/// +/// `kqueue` is able to monitor much more than just read/write readiness on file descriptors. Using +/// this extension trait, you can monitor for signals, process exits, and more. See the implementors +/// of the [`Filter`] trait for more information. +pub trait PollerKqueueExt: PollerSealed { + /// Add a filter to the poller. + /// + /// This is similar to [`add`][Poller::add], but it allows you to specify a filter instead of + /// a socket. See the implementors of the [`Filter`] trait for more information. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, PollMode}; + /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; + /// + /// let poller = Poller::new().unwrap(); + /// + /// // Register the SIGINT signal. + /// poller.add_filter(Signal::new(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); + /// + /// // Wait for the signal. + /// let mut events = vec![]; + /// poller.wait(&mut events, None).unwrap(); + /// # let _ = events; + /// ``` + fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()>; + + /// Modify a filter in the poller. + /// + /// This is similar to [`modify`][Poller::modify], but it allows you to specify a filter + /// instead of a socket. See the implementors of the [`Filter`] trait for more information. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, PollMode}; + /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; + /// + /// let poller = Poller::new().unwrap(); + /// + /// // Register the SIGINT signal. + /// poller.add_filter(Signal::new(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); + /// + /// // Re-register with a different key. + /// poller.modify_filter(Signal::new(libc::SIGINT), 1, PollMode::Oneshot).unwrap(); + /// + /// // Wait for the signal. + /// let mut events = vec![]; + /// poller.wait(&mut events, None).unwrap(); + /// # let _ = events; + /// ``` + fn modify_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()>; + + /// Remove a filter from the poller. + /// + /// This is used to remove filters that were previously added with + /// [`add_filter`](PollerKqueueExt::add_filter). + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, PollMode}; + /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; + /// + /// let poller = Poller::new().unwrap(); + /// + /// // Register the SIGINT signal. + /// poller.add_filter(Signal::new(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); + /// + /// // Remove the filter. + /// poller.delete_filter(Signal::new(libc::SIGINT)).unwrap(); + /// ``` + fn delete_filter(&self, filter: F) -> io::Result<()>; +} + +impl PollerSealed for Poller {} + +impl PollerKqueueExt for Poller { + fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> { + // No difference between adding and modifying in kqueue. + self.modify_filter(filter, key, mode) + } + + fn modify_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> { + // Convert the filter into a kevent. + let event = filter.filter(libc::EV_ADD | mode_to_flags(mode), key); + + // Modify the filter. + self.poller.submit_changes([event]) + } + + fn delete_filter(&self, filter: F) -> io::Result<()> { + // Convert the filter into a kevent. + let event = filter.filter(libc::EV_DELETE, 0); + + // Delete the filter. + self.poller.submit_changes([event]) + } +} + +/// A filter that can be registered into a `kqueue`. +pub trait Filter: FilterSealed {} + +unsafe impl FilterSealed for &T { + fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { + (**self).filter(flags, key) + } +} + +impl Filter for &T {} + +/// Monitor this signal number. +/// +/// No matter what `PollMode` is specified, this filter will always be +/// oneshot-only. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Signal(c_int); + +impl Signal { + /// Monitor this signal number. + pub fn new(signal: c_int) -> Self { + Self(signal) + } + + /// Get the signal number. + pub fn signal(self) -> c_int { + self.0 + } +} + +/// Alias for `libc::c_int`. +#[allow(non_camel_case_types)] +pub type c_int = i32; + +unsafe impl FilterSealed for Signal { + fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { + libc::kevent { + ident: self.0 as _, + filter: libc::EVFILT_SIGNAL, + flags: flags | libc::EV_RECEIPT, + udata: key as _, + ..unsafe { mem::zeroed() } + } + } +} + +impl Filter for Signal {} + +/// Monitor a child process. +#[derive(Debug)] +pub struct Process<'a> { + /// The child process to monitor. + child: &'a Child, + + /// The operation to monitor. + ops: ProcessOps, +} + +/// The operations that a monitored process can perform. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[non_exhaustive] +pub enum ProcessOps { + /// The process exited. + Exit, + + /// The process was forked. + Fork, + + /// The process executed a new process. + Exec, +} + +impl<'a> Process<'a> { + /// Monitor a child process. + pub fn new(child: &'a Child, ops: ProcessOps) -> Self { + Self { child, ops } + } +} + +unsafe impl FilterSealed for Process<'_> { + fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { + let fflags = match self.ops { + ProcessOps::Exit => libc::NOTE_EXIT, + ProcessOps::Fork => libc::NOTE_FORK, + ProcessOps::Exec => libc::NOTE_EXEC, + }; + + libc::kevent { + ident: self.child.id() as _, + filter: libc::EVFILT_PROC, + flags: flags | libc::EV_RECEIPT, + fflags, + udata: key as _, + ..unsafe { mem::zeroed() } + } + } +} + +impl Filter for Process<'_> {} + +/// Wait for a timeout to expire. +/// +/// Modifying the timeout after it has been added to the poller will reset it. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Timer { + /// Identifier for the timer. + id: usize, + + /// The timeout to wait for. + timeout: Duration, +} + +impl Timer { + /// Create a new timer. + pub fn new(id: usize, timeout: Duration) -> Self { + Self { id, timeout } + } + + /// Get the identifier for the timer. + pub fn id(self) -> usize { + self.id + } + + /// Get the timeout to wait for. + pub fn timeout(self) -> Duration { + self.timeout + } +} + +unsafe impl FilterSealed for Timer { + fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { + // Figure out the granularity of the timer. + let (fflags, data) = { + #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd")))] + { + let subsec_nanos = self.timeout.subsec_nanos(); + + match (subsec_nanos % 1_000, subsec_nanos % 1_000_000, subsec_nanos) { + (_, _, 0) => ( + libc::NOTE_SECONDS, + self.timeout.as_secs().try_into().expect("too many seconds"), + ), + (_, 0, _) => ( + // Note: 0 by default means milliseconds. + 0, + self.timeout + .as_millis() + .try_into() + .expect("too many milliseconds"), + ), + (0, _, _) => ( + libc::NOTE_USECONDS, + self.timeout + .as_micros() + .try_into() + .expect("too many microseconds"), + ), + (_, _, _) => ( + libc::NOTE_NSECONDS, + self.timeout + .as_nanos() + .try_into() + .expect("too many nanoseconds"), + ), + } + } + + #[cfg(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))] + { + // OpenBSD/Dragonfly/NetBSD only supports milliseconds. + // NetBSD 10 supports NOTE_SECONDS et al, once Rust drops support for + // NetBSD 9 we can use the same code as above. + // See also: https://github.com/rust-lang/libc/pull/3080 + ( + 0, + self.timeout + .as_millis() + .try_into() + .expect("too many milliseconds"), + ) + } + }; + + #[allow(clippy::needless_update)] + libc::kevent { + ident: self.id as _, + filter: libc::EVFILT_TIMER, + flags: flags | libc::EV_RECEIPT, + fflags, + data, + udata: key as _, + ..unsafe { mem::zeroed() } + } + } +} + +impl Filter for Timer {} + +mod __private { + use crate::sys::FilterFlags; + + #[doc(hidden)] + pub trait PollerSealed {} + + #[doc(hidden)] + pub unsafe trait FilterSealed { + /// Get the filter for the given event. + /// + /// This filter's flags must have `EV_RECEIPT`. + fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent; + } +} From 5987fa882a08167dad5fb524c5ec55d4b0f98ae0 Mon Sep 17 00:00:00 2001 From: jtnunley Date: Fri, 3 Feb 2023 10:29:12 -0800 Subject: [PATCH 6/9] Add inlines + move PollerSealed --- src/os.rs | 5 +++++ src/os/kqueue.rs | 12 ++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/os.rs b/src/os.rs index 1e68e46..280adf3 100644 --- a/src/os.rs +++ b/src/os.rs @@ -14,3 +14,8 @@ not(polling_test_poll_backend), ))] pub mod kqueue; + +mod __private { + #[doc(hidden)] + pub trait PollerSealed {} +} diff --git a/src/os/kqueue.rs b/src/os/kqueue.rs index cd3617c..9780feb 100644 --- a/src/os/kqueue.rs +++ b/src/os/kqueue.rs @@ -2,12 +2,15 @@ use crate::sys::{mode_to_flags, FilterFlags}; use crate::{PollMode, Poller}; -use __private::{FilterSealed, PollerSealed}; + use std::convert::TryInto; use std::process::Child; use std::time::Duration; use std::{io, mem}; +use __private::FilterSealed; +use super::__private::PollerSealed; + // TODO(notgull): We should also have EVFILT_AIO, EVFILT_VNODE and EVFILT_USER. However, the current // API makes it difficult to effectively express events from these filters. At the next breaking // change, we should change `Event` to be a struct with private fields, and encode additional @@ -93,6 +96,7 @@ pub trait PollerKqueueExt: PollerSealed { impl PollerSealed for Poller {} impl PollerKqueueExt for Poller { + #[inline(always)] fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> { // No difference between adding and modifying in kqueue. self.modify_filter(filter, key, mode) @@ -119,6 +123,7 @@ impl PollerKqueueExt for Poller { pub trait Filter: FilterSealed {} unsafe impl FilterSealed for &T { + #[inline(always)] fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { (**self).filter(flags, key) } @@ -150,6 +155,7 @@ impl Signal { pub type c_int = i32; unsafe impl FilterSealed for Signal { + #[inline(always)] fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { libc::kevent { ident: self.0 as _, @@ -195,6 +201,7 @@ impl<'a> Process<'a> { } unsafe impl FilterSealed for Process<'_> { + #[inline(always)] fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent { let fflags = match self.ops { ProcessOps::Exit => libc::NOTE_EXIT, @@ -316,9 +323,6 @@ impl Filter for Timer {} mod __private { use crate::sys::FilterFlags; - #[doc(hidden)] - pub trait PollerSealed {} - #[doc(hidden)] pub unsafe trait FilterSealed { /// Get the filter for the given event. From 9a1bf975552864f598e1c28ba211322f8c6903b8 Mon Sep 17 00:00:00 2001 From: jtnunley Date: Fri, 3 Feb 2023 10:30:53 -0800 Subject: [PATCH 7/9] rustfmt --- src/os/kqueue.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/os/kqueue.rs b/src/os/kqueue.rs index 9780feb..2acd194 100644 --- a/src/os/kqueue.rs +++ b/src/os/kqueue.rs @@ -8,8 +8,8 @@ use std::process::Child; use std::time::Duration; use std::{io, mem}; -use __private::FilterSealed; use super::__private::PollerSealed; +use __private::FilterSealed; // TODO(notgull): We should also have EVFILT_AIO, EVFILT_VNODE and EVFILT_USER. However, the current // API makes it difficult to effectively express events from these filters. At the next breaking From 9d09ad35bfa619ebb0ccb306290d10b4337a1d95 Mon Sep 17 00:00:00 2001 From: jtnunley Date: Fri, 3 Feb 2023 10:37:56 -0800 Subject: [PATCH 8/9] Make filter fields public --- src/os/kqueue.rs | 35 +++-------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/src/os/kqueue.rs b/src/os/kqueue.rs index 2acd194..ca7a112 100644 --- a/src/os/kqueue.rs +++ b/src/os/kqueue.rs @@ -136,19 +136,7 @@ impl Filter for &T {} /// No matter what `PollMode` is specified, this filter will always be /// oneshot-only. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Signal(c_int); - -impl Signal { - /// Monitor this signal number. - pub fn new(signal: c_int) -> Self { - Self(signal) - } - - /// Get the signal number. - pub fn signal(self) -> c_int { - self.0 - } -} +pub struct Signal(pub c_int); /// Alias for `libc::c_int`. #[allow(non_camel_case_types)] @@ -228,27 +216,10 @@ impl Filter for Process<'_> {} #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Timer { /// Identifier for the timer. - id: usize, + pub id: usize, /// The timeout to wait for. - timeout: Duration, -} - -impl Timer { - /// Create a new timer. - pub fn new(id: usize, timeout: Duration) -> Self { - Self { id, timeout } - } - - /// Get the identifier for the timer. - pub fn id(self) -> usize { - self.id - } - - /// Get the timeout to wait for. - pub fn timeout(self) -> Duration { - self.timeout - } + pub timeout: Duration, } unsafe impl FilterSealed for Timer { From 2a433eb3c40bb2448e93f9c130203f5dd5a1ce91 Mon Sep 17 00:00:00 2001 From: jtnunley Date: Fri, 3 Feb 2023 10:40:10 -0800 Subject: [PATCH 9/9] Fix examples --- examples/wait-signal.rs | 2 +- src/os/kqueue.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/wait-signal.rs b/examples/wait-signal.rs index eb1eb65..02a4c5d 100644 --- a/examples/wait-signal.rs +++ b/examples/wait-signal.rs @@ -20,7 +20,7 @@ mod example { let poller = Poller::new().unwrap(); // Register SIGINT in the poller. - let sigint = Signal::new(libc::SIGINT); + let sigint = Signal(libc::SIGINT); poller.add_filter(sigint, 1, PollMode::Oneshot).unwrap(); let mut events = vec![]; diff --git a/src/os/kqueue.rs b/src/os/kqueue.rs index ca7a112..ad6aa1d 100644 --- a/src/os/kqueue.rs +++ b/src/os/kqueue.rs @@ -36,7 +36,7 @@ pub trait PollerKqueueExt: PollerSealed { /// let poller = Poller::new().unwrap(); /// /// // Register the SIGINT signal. - /// poller.add_filter(Signal::new(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); + /// poller.add_filter(Signal(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); /// /// // Wait for the signal. /// let mut events = vec![]; @@ -59,10 +59,10 @@ pub trait PollerKqueueExt: PollerSealed { /// let poller = Poller::new().unwrap(); /// /// // Register the SIGINT signal. - /// poller.add_filter(Signal::new(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); + /// poller.add_filter(Signal(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); /// /// // Re-register with a different key. - /// poller.modify_filter(Signal::new(libc::SIGINT), 1, PollMode::Oneshot).unwrap(); + /// poller.modify_filter(Signal(libc::SIGINT), 1, PollMode::Oneshot).unwrap(); /// /// // Wait for the signal. /// let mut events = vec![]; @@ -85,10 +85,10 @@ pub trait PollerKqueueExt: PollerSealed { /// let poller = Poller::new().unwrap(); /// /// // Register the SIGINT signal. - /// poller.add_filter(Signal::new(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); + /// poller.add_filter(Signal(libc::SIGINT), 0, PollMode::Oneshot).unwrap(); /// /// // Remove the filter. - /// poller.delete_filter(Signal::new(libc::SIGINT)).unwrap(); + /// poller.delete_filter(Signal(libc::SIGINT)).unwrap(); /// ``` fn delete_filter(&self, filter: F) -> io::Result<()>; }