Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/event/kqueue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ impl Event {
flags,
user_flags,
} => (ident as _, 0, c::EVFILT_USER, flags.bits() | user_flags.0),
#[cfg(apple)]
EventFilter::MachPort { port } => (port as _, 0, c::EVFILT_MACHPORT, 0),
EventFilter::Unknown => panic!("unknown filter"),
};

Expand Down Expand Up @@ -150,6 +152,10 @@ impl Event {
flags: UserFlags::from_bits_retain(self.inner.fflags),
user_flags: UserDefinedFlags(self.inner.fflags & EVFILT_USER_FLAGS),
},
#[cfg(apple)]
c::EVFILT_MACHPORT => EventFilter::MachPort {
port: self.inner.ident as _,
},
_ => EventFilter::Unknown,
}
}
Expand All @@ -159,6 +165,13 @@ impl Event {
#[cfg(any(apple, freebsdlike))]
const EVFILT_USER_FLAGS: u32 = 0x00ff_ffff;

/// The name of a Mach port, as `EVFILT_MACHPORT` identifies it.
///
/// This is a `mach_port_name_t` rather than a file descriptor, so it is kept
/// distinct from `RawFd`.
#[cfg(apple)]
pub type RawMachPort = u32;

/// The possible filters for a `kqueue`.
#[repr(i16)]
#[non_exhaustive]
Expand Down Expand Up @@ -210,6 +223,17 @@ pub enum EventFilter {
timer: Option<Duration>,
},

/// A Mach port filter.
///
/// Reports that a message is waiting on a Mach port or port set, without
/// dequeuing it. The identifier is a port name rather than a file
/// descriptor, and the port must hold a receive right.
#[cfg(apple)]
MachPort {
/// The name of the port or port set to watch.
port: RawMachPort,
},

/// A user filter.
#[cfg(any(apple, freebsdlike))]
User {
Expand Down
99 changes: 99 additions & 0 deletions tests/event/kqueue_machport.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//! Tests for `EventFilter::MachPort`.

use std::mem::MaybeUninit;
use std::ptr::null_mut;

use rustix::event::kqueue::{kevent, kqueue, Event, EventFilter, EventFlags};

type KernReturn = libc::c_int;

extern "C" {
static mach_task_self_: u32;
fn mach_port_allocate(task: u32, right: libc::c_uint, name: *mut u32) -> KernReturn;
fn mach_port_insert_right(
task: u32,
name: u32,
poly: u32,
poly_poly: libc::c_uint,
) -> KernReturn;
fn mach_msg(
msg: *mut MachMsgHeader,
option: libc::c_int,
send_size: u32,
rcv_size: u32,
rcv_name: u32,
timeout: u32,
notify: u32,
) -> KernReturn;
}

#[repr(C)]
#[derive(Clone, Copy, Default)]
struct MachMsgHeader {
bits: u32,
size: u32,
remote_port: u32,
local_port: u32,
voucher_port: u32,
id: i32,
}

/// A port with a message queued is reported ready, and the message is left in
/// place for the caller to receive itself.
#[test]
fn test_kqueue_machport() {
const MACH_PORT_RIGHT_RECEIVE: libc::c_uint = 1;
const MACH_MSG_TYPE_MAKE_SEND: libc::c_uint = 20;
const MACH_MSG_TYPE_COPY_SEND: u32 = 19;
const MACH_SEND_MSG: libc::c_int = 1;

let mut port = 0u32;
unsafe {
assert_eq!(
mach_port_allocate(mach_task_self_, MACH_PORT_RIGHT_RECEIVE, &mut port),
0
);
assert_eq!(
mach_port_insert_right(mach_task_self_, port, port, MACH_MSG_TYPE_MAKE_SEND),
0
);
}

let queue = kqueue().unwrap();
let change = Event::new(
EventFilter::MachPort { port },
EventFlags::ADD | EventFlags::ENABLE,
null_mut(),
);
// Registering only: an events buffer with no timeout would block here.
let mut none: [MaybeUninit<Event>; 0] = [];
unsafe { kevent(&queue, &[change], &mut none[..], None).unwrap() };

// Nothing has been sent, so nothing is ready.
let mut events = [MaybeUninit::<Event>::uninit(); 1];
let (ready, _) =
unsafe { kevent(&queue, &[], &mut events[..], Some(Default::default())).unwrap() };
assert!(ready.is_empty());

let size = core::mem::size_of::<MachMsgHeader>() as u32;
let mut message = MachMsgHeader {
bits: MACH_MSG_TYPE_COPY_SEND,
size,
remote_port: port,
id: 1234,
..Default::default()
};
assert_eq!(
unsafe { mach_msg(&mut message, MACH_SEND_MSG, size, 0, 0, 0, 0) },
0
);

let mut events = [MaybeUninit::<Event>::uninit(); 1];
let (ready, _) =
unsafe { kevent(&queue, &[], &mut events[..], Some(Default::default())).unwrap() };
assert_eq!(ready.len(), 1);
assert!(matches!(
ready[0].filter(),
EventFilter::MachPort { port: p } if p == port
));
}
2 changes: 2 additions & 0 deletions tests/event/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ mod epoll_timeout;
#[cfg(not(windows))]
#[cfg(not(target_os = "wasi"))]
mod eventfd;
#[cfg(apple)]
mod kqueue_machport;
mod poll;
#[cfg(solarish)]
mod port;
Expand Down
Loading