use std::os::unix::io::RawFd;
pub struct EventFd {
fd: RawFd,
}
impl EventFd {
pub fn new() -> crate::Result<Self> {
let fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_SEMAPHORE) };
if fd < 0 {
return Err(crate::Error::Io(std::io::Error::last_os_error()));
}
Ok(Self { fd })
}
pub fn as_raw_fd(&self) -> RawFd {
self.fd
}
pub fn drain(&self) -> u64 {
let mut buf = 0u64;
let ret = unsafe {
libc::read(
self.fd,
&mut buf as *mut u64 as *mut libc::c_void,
std::mem::size_of::<u64>(),
)
};
if ret == 8 { buf } else { 0 }
}
pub fn poll_wait(&self, timeout_ms: i32) -> bool {
let mut pfd = libc::pollfd {
fd: self.fd,
events: libc::POLLIN,
revents: 0,
};
let ret = unsafe { libc::poll(&mut pfd, 1, timeout_ms) };
ret > 0 && (pfd.revents & libc::POLLIN) != 0
}
pub fn notifier(&self) -> EventFdNotifier {
EventFdNotifier { fd: self.fd }
}
}
impl Drop for EventFd {
fn drop(&mut self) {
unsafe {
libc::close(self.fd);
}
}
}
#[derive(Clone, Copy)]
pub struct EventFdNotifier {
fd: RawFd,
}
unsafe impl Send for EventFdNotifier {}
unsafe impl Sync for EventFdNotifier {}
impl EventFdNotifier {
pub fn notify(&self) {
let val: u64 = 1;
unsafe {
libc::write(
self.fd,
&val as *const u64 as *const libc::c_void,
std::mem::size_of::<u64>(),
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_and_signal() {
let efd = EventFd::new().unwrap();
let notifier = efd.notifier();
assert_eq!(efd.drain(), 0);
notifier.notify();
assert_eq!(efd.drain(), 1);
assert_eq!(efd.drain(), 0);
}
#[test]
fn multiple_signals_accumulate() {
let efd = EventFd::new().unwrap();
let notifier = efd.notifier();
notifier.notify();
notifier.notify();
notifier.notify();
assert_eq!(efd.drain(), 1);
assert_eq!(efd.drain(), 1);
assert_eq!(efd.drain(), 1);
assert_eq!(efd.drain(), 0);
}
#[test]
fn poll_wait_timeout() {
let efd = EventFd::new().unwrap();
assert!(!efd.poll_wait(1));
}
#[test]
fn poll_wait_signaled() {
let efd = EventFd::new().unwrap();
let notifier = efd.notifier();
notifier.notify();
assert!(efd.poll_wait(100));
}
#[test]
fn notifier_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<EventFdNotifier>();
}
}