use std::io;
use std::os::unix::io::{AsRawFd, RawFd};
use std::ptr;
use std::time::Duration;
#[cfg(not(polling_no_io_safety))]
use std::os::unix::io::{AsFd, BorrowedFd};
use crate::{Event, PollMode};
#[derive(Debug)]
pub struct Poller {
port_fd: RawFd,
}
impl Poller {
pub fn new() -> io::Result<Poller> {
let port_fd = syscall!(port_create())?;
let flags = syscall!(fcntl(port_fd, libc::F_GETFD))?;
syscall!(fcntl(port_fd, libc::F_SETFD, flags | libc::FD_CLOEXEC))?;
Ok(Poller { port_fd })
}
pub fn supports_level(&self) -> bool {
false
}
pub fn supports_edge(&self) -> bool {
false
}
pub fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> {
self.modify(fd, ev, mode)
}
pub fn modify(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> {
let mut flags = 0;
if ev.readable {
flags |= libc::POLLIN;
}
if ev.writable {
flags |= libc::POLLOUT;
}
if mode != PollMode::Oneshot {
return Err(crate::unsupported_error(
"this kind of event is not supported with event ports",
));
}
syscall!(port_associate(
self.port_fd,
libc::PORT_SOURCE_FD,
fd as _,
flags as _,
ev.key as _,
))?;
Ok(())
}
pub fn delete(&self, fd: RawFd) -> io::Result<()> {
if let Err(e) = syscall!(port_dissociate(
self.port_fd,
libc::PORT_SOURCE_FD,
fd as usize,
)) {
match e.raw_os_error().unwrap() {
libc::ENOENT => return Ok(()),
_ => return Err(e),
}
}
Ok(())
}
pub fn wait(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
let mut timeout = timeout.map(|t| libc::timespec {
tv_sec: t.as_secs() as libc::time_t,
tv_nsec: t.subsec_nanos() as libc::c_long,
});
let mut nget: u32 = 1;
let res = syscall!(port_getn(
self.port_fd,
events.list.as_mut_ptr() as *mut libc::port_event,
events.list.len() as libc::c_uint,
&mut nget as _,
match &mut timeout {
None => ptr::null_mut(),
Some(t) => t,
}
));
let nevents = match res {
Err(e) => match e.raw_os_error().unwrap() {
libc::ETIME => 0,
_ => return Err(e),
},
Ok(_) => nget as usize,
};
events.len = nevents;
Ok(())
}
pub fn notify(&self) -> io::Result<()> {
syscall!(port_send(
self.port_fd,
libc::PORT_SOURCE_USER,
crate::NOTIFY_KEY as _
))?;
Ok(())
}
}
impl AsRawFd for Poller {
fn as_raw_fd(&self) -> RawFd {
self.port_fd
}
}
#[cfg(not(polling_no_io_safety))]
impl AsFd for Poller {
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.port_fd) }
}
}
impl Drop for Poller {
fn drop(&mut self) {
let _ = syscall!(close(self.port_fd));
}
}
fn read_flags() -> libc::c_short {
libc::POLLIN | libc::POLLHUP | libc::POLLERR | libc::POLLPRI
}
fn write_flags() -> libc::c_short {
libc::POLLOUT | libc::POLLHUP | libc::POLLERR
}
pub struct Events {
list: Box<[libc::port_event; 1024]>,
len: usize,
}
unsafe impl Send for Events {}
impl Events {
pub fn new() -> Events {
let ev = libc::port_event {
portev_events: 0,
portev_source: 0,
portev_pad: 0,
portev_object: 0,
portev_user: 0 as _,
};
let list = Box::new([ev; 1024]);
let len = 0;
Events { list, len }
}
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
self.list[..self.len].iter().map(|ev| Event {
key: ev.portev_user as _,
readable: (ev.portev_events & read_flags() as libc::c_int) != 0,
writable: (ev.portev_events & write_flags() as libc::c_int) != 0,
})
}
}