use super::{Notify, FULL, PARTIAL};
use crate::Error;
use core::mem::{self, MaybeUninit};
use core::ptr;
use core::time::Duration;
#[repr(C)]
pub struct EventFd {
fd: i32,
}
impl Default for EventFd {
fn default() -> Self {
let fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) };
assert!(fd > -1, "EventFd::default {}", Error::last());
Self { fd }
}
}
impl EventFd {
fn write(&self, val: u64) -> isize {
unsafe {
libc::write(
self.fd,
ptr::addr_of!(val).cast::<libc::c_void>(),
mem::size_of_val(&val),
)
}
}
fn read(&self) -> u64 {
let mut val = 0_u64;
unsafe {
libc::read(
self.fd,
ptr::addr_of_mut!(val).cast::<libc::c_void>(),
mem::size_of_val(&val),
);
}
val
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn wait(&self, read: bool, timeout: Option<Duration>) -> Option<Duration> {
let mut tm = libc::timeval {
tv_sec: 0,
tv_usec: 0,
};
let mut set = MaybeUninit::<libc::fd_set>::zeroed();
unsafe {
libc::FD_SET(self.fd, set.as_mut_ptr());
}
let null_set = ptr::null_mut::<libc::fd_set>();
let tm_ptr = if let Some(timeout) = timeout {
tm.tv_sec = timeout.as_secs() as libc::time_t;
tm.tv_usec = timeout.subsec_micros() as libc::suseconds_t;
ptr::addr_of_mut!(tm)
} else {
ptr::null_mut::<libc::timeval>()
};
let (rd_set, wr_set) = if read {
(set.as_mut_ptr(), null_set)
} else {
(null_set, set.as_mut_ptr())
};
unsafe { libc::select(self.fd + 1, rd_set, wr_set, null_set, tm_ptr) };
if tm_ptr.is_null() {
None
} else {
Some(Duration::new(tm.tv_sec as u64, (tm.tv_usec * 1000) as u32))
}
}
}
impl Drop for EventFd {
fn drop(&mut self) {
unsafe { libc::close(self.fd) };
}
}
impl Notify for EventFd {
fn notify(&self, from: u8, to: u8) {
assert!(from != to);
if from == PARTIAL {
if to == FULL {
self.write(u64::MAX >> 1);
} else {
self.read();
}
} else if from == FULL {
self.read();
if to == PARTIAL {
self.write(u64::MAX >> 1);
}
} else if to == PARTIAL {
self.write(u64::MAX >> 1);
} else {
self.write(u64::MAX - 1);
}
}
fn wait_read(&self, timeout: Option<Duration>) -> Option<Duration> {
self.wait(true, timeout)
}
fn wait_write(&self, timeout: Option<Duration>) -> Option<Duration> {
self.wait(false, timeout)
}
fn read_fd_event(&self) -> (i32, bool) {
(self.fd, true)
}
fn write_fd_event(&self) -> (i32, bool) {
(self.fd, true)
}
}