use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
pub struct EventFd {
fd: OwnedFd,
}
impl EventFd {
pub fn new() -> io::Result<Self> {
let fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) };
if fd < 0 {
return Err(io::Error::last_os_error());
}
let fd = unsafe { OwnedFd::from_raw_fd(fd) };
Ok(Self { fd })
}
pub fn notify(&self) -> io::Result<()> {
let val: u64 = 1;
let ret = unsafe {
libc::write(
self.fd.as_raw_fd(),
&val as *const u64 as *const libc::c_void,
8,
)
};
if ret < 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn try_read(&self) -> io::Result<u64> {
let mut val: u64 = 0;
let ret = unsafe {
libc::read(
self.fd.as_raw_fd(),
&mut val as *mut u64 as *mut libc::c_void,
8,
)
};
if ret < 0 {
let err = io::Error::last_os_error();
if err.kind() == io::ErrorKind::WouldBlock {
Ok(0)
} else {
Err(err)
}
} else {
Ok(val)
}
}
pub fn as_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}
unsafe impl Send for EventFd {}
unsafe impl Sync for EventFd {}
pub struct WakePair {
pub producer_wake: EventFd,
pub consumer_wake: EventFd,
}
impl WakePair {
pub fn new() -> io::Result<Self> {
Ok(Self {
producer_wake: EventFd::new()?,
consumer_wake: EventFd::new()?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn notify_and_read() {
let efd = EventFd::new().unwrap();
assert_eq!(efd.try_read().unwrap(), 0);
efd.notify().unwrap();
assert_eq!(efd.try_read().unwrap(), 1);
assert_eq!(efd.try_read().unwrap(), 0);
}
#[test]
fn multiple_notifies_accumulate() {
let efd = EventFd::new().unwrap();
efd.notify().unwrap();
efd.notify().unwrap();
efd.notify().unwrap();
assert_eq!(efd.try_read().unwrap(), 3);
assert_eq!(efd.try_read().unwrap(), 0);
}
#[test]
fn wake_pair_bidirectional() {
let pair = WakePair::new().unwrap();
pair.consumer_wake.notify().unwrap();
assert_eq!(pair.consumer_wake.try_read().unwrap(), 1);
pair.producer_wake.notify().unwrap();
assert_eq!(pair.producer_wake.try_read().unwrap(), 1);
}
#[test]
fn fd_is_valid() {
let efd = EventFd::new().unwrap();
assert!(efd.as_fd() >= 0);
}
}