Skip to content

Commit 3407489

Browse files
event: add kevent64 support on apple targets (#2781)
* event: add kevent64 support on apple targets Add KEvent64, Kevent64Flags, and Kqueue::kevent64 wrapping the kevent64 syscall available on apple platforms. This uses fixed-width 64-bit fields and provides an additional ext field for filter-specific data. * docs: correct doc of struct KEvent * test: Kqueue.kevent64() --------- Co-authored-by: Steve Lau <stevelauc@outlook.com>
1 parent fe80fc5 commit 3407489

4 files changed

Lines changed: 166 additions & 3 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ targets = [
2727
]
2828

2929
[dependencies]
30-
libc = { version = "0.2.183", features = ["extra_traits"] }
30+
libc = { version = "0.2.186", features = ["extra_traits"] }
3131
bitflags = "2.3.3"
3232
cfg-if = "1.0"
3333
pin-utils = { version = "0.1.0", optional = true }

changelog/2781.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added `kevent64` support on apple targets: `Kqueue::kevent64`, `KEvent64`, and `Kevent64Flags`.

src/sys/event.rs

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,21 @@ use std::os::fd::{AsFd, BorrowedFd};
1414
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd};
1515
use std::ptr;
1616

17-
/// A kernel event queue. Used to notify a process of various asynchronous
18-
/// events.
17+
/// An event for use with [`Kqueue::kevent`].
1918
#[repr(C)]
2019
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
2120
pub struct KEvent {
2221
kevent: libc::kevent,
2322
}
2423

24+
/// An event for use with [`Kqueue::kevent64`].
25+
#[cfg(apple_targets)]
26+
#[repr(transparent)]
27+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
28+
pub struct KEvent64 {
29+
kevent: libc::kevent64_s,
30+
}
31+
2532
/// A kernel event queue.
2633
///
2734
/// Used by the kernel to notify the process of various types of asynchronous
@@ -89,6 +96,33 @@ impl Kqueue {
8996
};
9097
Errno::result(res).map(|r| r as usize)
9198
}
99+
100+
/// Like [`kevent`](Kqueue::kevent), but uses [`KEvent64`].
101+
#[cfg(apple_targets)]
102+
pub fn kevent64(
103+
&self,
104+
changelist: &[KEvent64],
105+
eventlist: &mut [KEvent64],
106+
flags: Kevent64Flags,
107+
timeout_opt: Option<timespec>,
108+
) -> Result<usize> {
109+
let res = unsafe {
110+
libc::kevent64(
111+
self.0.as_raw_fd(),
112+
changelist.as_ptr().cast(),
113+
changelist.len() as c_int,
114+
eventlist.as_mut_ptr().cast(),
115+
eventlist.len() as c_int,
116+
flags.bits(),
117+
if let Some(ref timeout) = timeout_opt {
118+
timeout as *const timespec
119+
} else {
120+
ptr::null()
121+
},
122+
)
123+
};
124+
Errno::result(res).map(|r| r as usize)
125+
}
92126
}
93127

94128
#[cfg(target_os = "netbsd")]
@@ -328,6 +362,17 @@ libc_bitflags!(
328362
}
329363
);
330364

365+
#[cfg(apple_targets)]
366+
libc_bitflags! {
367+
/// Flags for [`Kqueue::kevent64`].
368+
pub struct Kevent64Flags: libc::c_uint {
369+
/// Return immediately even if there are no events to return.
370+
KEVENT_FLAG_IMMEDIATE;
371+
/// Only return error events from the changelist.
372+
KEVENT_FLAG_ERROR_EVENTS;
373+
}
374+
}
375+
331376
#[allow(missing_docs)]
332377
#[deprecated(since = "0.27.0", note = "Use KEvent::new instead")]
333378
pub fn kqueue() -> Result<Kqueue> {
@@ -401,6 +446,75 @@ impl KEvent {
401446
}
402447
}
403448

449+
#[cfg(apple_targets)]
450+
const _: () = {
451+
fn _assert_send<T: Send>() {}
452+
fn _assert() {
453+
_assert_send::<KEvent64>();
454+
}
455+
};
456+
457+
#[cfg(apple_targets)]
458+
impl KEvent64 {
459+
/// Construct a new `KEvent64`.
460+
pub fn new(
461+
ident: u64,
462+
filter: EventFilter,
463+
flags: EvFlags,
464+
fflags: FilterFlag,
465+
data: i64,
466+
udata: u64,
467+
ext: [u64; 2],
468+
) -> KEvent64 {
469+
KEvent64 {
470+
kevent: libc::kevent64_s {
471+
ident,
472+
filter: filter as type_of_event_filter,
473+
flags: flags.bits(),
474+
fflags: fflags.bits(),
475+
data,
476+
udata,
477+
ext,
478+
},
479+
}
480+
}
481+
482+
/// Value used to identify this event.
483+
pub fn ident(&self) -> u64 {
484+
self.kevent.ident
485+
}
486+
487+
/// Identifies the kernel filter used to process this event.
488+
pub fn filter(&self) -> Result<EventFilter> {
489+
self.kevent.filter.try_into()
490+
}
491+
492+
/// Event flags.
493+
pub fn flags(&self) -> EvFlags {
494+
EvFlags::from_bits_retain(self.kevent.flags)
495+
}
496+
497+
/// Filter-specific flags.
498+
pub fn fflags(&self) -> FilterFlag {
499+
FilterFlag::from_bits_retain(self.kevent.fflags)
500+
}
501+
502+
/// Filter-specific data value.
503+
pub fn data(&self) -> i64 {
504+
self.kevent.data
505+
}
506+
507+
/// Opaque user-defined value passed through the kernel unchanged.
508+
pub fn udata(&self) -> u64 {
509+
self.kevent.udata
510+
}
511+
512+
/// Filter-specific extension data.
513+
pub fn ext(&self) -> [u64; 2] {
514+
self.kevent.ext
515+
}
516+
}
517+
404518
#[allow(missing_docs)]
405519
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
406520
pub fn kevent(

test/sys/test_event.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use libc::intptr_t;
22
use nix::sys::event::{EvFlags, EventFilter, FilterFlag, KEvent};
3+
#[cfg(apple_targets)]
4+
use nix::sys::event::{KEvent64, Kevent64Flags, Kqueue};
35

46
#[test]
57
fn test_struct_kevent() {
@@ -25,6 +27,33 @@ fn test_struct_kevent() {
2527
assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
2628
}
2729

30+
#[cfg(apple_targets)]
31+
#[test]
32+
fn test_struct_kevent64() {
33+
use std::mem;
34+
35+
let actual = KEvent64::new(
36+
0xdead_beef,
37+
EventFilter::EVFILT_READ,
38+
EvFlags::EV_ONESHOT | EvFlags::EV_ADD,
39+
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
40+
0x1337,
41+
12345,
42+
[0xaa, 0xbb],
43+
);
44+
assert_eq!(0xdead_beef, actual.ident());
45+
assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
46+
assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
47+
assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
48+
assert_eq!(0x1337, actual.data());
49+
assert_eq!(12345, actual.udata());
50+
assert_eq!([0xaa, 0xbb], actual.ext());
51+
assert_eq!(
52+
mem::size_of::<libc::kevent64_s>(),
53+
mem::size_of::<KEvent64>()
54+
);
55+
}
56+
2857
#[test]
2958
fn test_kevent_filter() {
3059
let udata: intptr_t = 12345;
@@ -39,3 +68,22 @@ fn test_kevent_filter() {
3968
);
4069
assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
4170
}
71+
72+
#[test]
73+
#[cfg(apple_targets)]
74+
fn kevent64_empty_lists_zero_timeout() {
75+
let kq = Kqueue::new().expect("failed to create kqueue");
76+
let changelist: [KEvent64; 0] = [];
77+
let mut eventlist: [KEvent64; 0] = [];
78+
let timeout = libc::timespec {
79+
tv_sec: 0,
80+
tv_nsec: 0,
81+
};
82+
let result = kq.kevent64(
83+
&changelist,
84+
&mut eventlist,
85+
Kevent64Flags::empty(),
86+
Some(timeout),
87+
);
88+
assert_eq!(result.expect("kevent64 should succeed"), 0);
89+
}

0 commit comments

Comments
 (0)