1 use std::fs::File; 2 use std::io::{self, Read, Write}; 3 #[cfg(not(target_os = "hermit"))] 4 use std::os::fd::{AsRawFd, FromRawFd, RawFd}; 5 // TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this 6 // can use `std::os::fd` and be merged with the above. 7 #[cfg(target_os = "hermit")] 8 use std::os::hermit::io::{AsRawFd, FromRawFd, RawFd}; 9 10 use crate::sys::Selector; 11 use crate::{Interest, Token}; 12 13 /// Waker backed by `eventfd`. 14 /// 15 /// `eventfd` is effectively an 64 bit counter. All writes must be of 8 16 /// bytes (64 bits) and are converted (native endian) into an 64 bit 17 /// unsigned integer and added to the count. Reads must also be 8 bytes and 18 /// reset the count to 0, returning the count. 19 #[derive(Debug)] 20 pub(crate) struct Waker { 21 fd: File, 22 } 23 24 impl Waker { 25 #[allow(dead_code)] // Not used by the `poll(2)` implementation. new(selector: &Selector, token: Token) -> io::Result<Waker>26 pub(crate) fn new(selector: &Selector, token: Token) -> io::Result<Waker> { 27 let waker = Waker::new_unregistered()?; 28 selector.register(waker.fd.as_raw_fd(), token, Interest::READABLE)?; 29 Ok(waker) 30 } 31 new_unregistered() -> io::Result<Waker>32 pub(crate) fn new_unregistered() -> io::Result<Waker> { 33 #[cfg(not(target_os = "espidf"))] 34 let flags = libc::EFD_CLOEXEC | libc::EFD_NONBLOCK; 35 // ESP-IDF is EFD_NONBLOCK by default and errors if you try to pass this flag. 36 #[cfg(target_os = "espidf")] 37 let flags = 0; 38 let fd = syscall!(eventfd(0, flags))?; 39 let file = unsafe { File::from_raw_fd(fd) }; 40 Ok(Waker { fd: file }) 41 } 42 43 #[allow(clippy::unused_io_amount)] // Don't care about partial writes. wake(&self) -> io::Result<()>44 pub(crate) fn wake(&self) -> io::Result<()> { 45 // The epoll emulation on some illumos systems currently requires 46 // the eventfd to be read before an edge-triggered read event is 47 // generated. 48 // See https://www.illumos.org/issues/16700. 49 #[cfg(target_os = "illumos")] 50 self.reset()?; 51 52 let buf: [u8; 8] = 1u64.to_ne_bytes(); 53 match (&self.fd).write(&buf) { 54 Ok(_) => Ok(()), 55 Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { 56 // Writing only blocks if the counter is going to overflow. 57 // So we'll reset the counter to 0 and wake it again. 58 self.reset()?; 59 self.wake() 60 } 61 Err(err) => Err(err), 62 } 63 } 64 65 #[allow(dead_code)] // Only used by the `poll(2)` implementation. ack_and_reset(&self)66 pub(crate) fn ack_and_reset(&self) { 67 let _ = self.reset(); 68 } 69 70 /// Reset the eventfd object, only need to call this if `wake` fails. 71 #[allow(clippy::unused_io_amount)] // Don't care about partial reads. reset(&self) -> io::Result<()>72 fn reset(&self) -> io::Result<()> { 73 let mut buf: [u8; 8] = 0u64.to_ne_bytes(); 74 match (&self.fd).read(&mut buf) { 75 Ok(_) => Ok(()), 76 // If the `Waker` hasn't been awoken yet this will return a 77 // `WouldBlock` error which we can safely ignore. 78 Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Ok(()), 79 Err(err) => Err(err), 80 } 81 } 82 } 83 84 impl AsRawFd for Waker { as_raw_fd(&self) -> RawFd85 fn as_raw_fd(&self) -> RawFd { 86 self.fd.as_raw_fd() 87 } 88 } 89