#[cfg(all( not(mio_unsupported_force_poll_poll), not(all( not(mio_unsupported_force_waker_pipe), any( target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "tvos", target_os = "watchos", ) )), not(any(target_os = "solaris", target_os = "vita")), ))] mod fdbased { #[cfg(all( not(mio_unsupported_force_waker_pipe), any(target_os = "linux", target_os = "android"), ))] use crate::sys::unix::waker::eventfd::WakerInternal; #[cfg(any( mio_unsupported_force_waker_pipe, target_os = "aix", target_os = "dragonfly", target_os = "illumos", target_os = "netbsd", target_os = "openbsd", target_os = "redox", ))] use crate::sys::unix::waker::pipe::WakerInternal; use crate::sys::Selector; use crate::{Interest, Token}; use std::io; use std::os::unix::io::AsRawFd; #[derive(Debug)] pub struct Waker { waker: WakerInternal, } impl Waker { pub fn new(selector: &Selector, token: Token) -> io::Result { let waker = WakerInternal::new()?; selector.register(waker.as_raw_fd(), token, Interest::READABLE)?; Ok(Waker { waker }) } pub fn wake(&self) -> io::Result<()> { self.waker.wake() } } } #[cfg(all( not(mio_unsupported_force_poll_poll), not(all( not(mio_unsupported_force_waker_pipe), any( target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "tvos", target_os = "watchos", ) )), not(any(target_os = "solaris", target_os = "vita")), ))] pub use self::fdbased::Waker; #[cfg(all( not(mio_unsupported_force_waker_pipe), any(target_os = "linux", target_os = "android", target_os = "espidf") ))] mod eventfd { use std::fs::File; use std::io::{self, Read, Write}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; /// Waker backed by `eventfd`. /// /// `eventfd` is effectively an 64 bit counter. All writes must be of 8 /// bytes (64 bits) and are converted (native endian) into an 64 bit /// unsigned integer and added to the count. Reads must also be 8 bytes and /// reset the count to 0, returning the count. #[derive(Debug)] pub struct WakerInternal { fd: File, } impl WakerInternal { pub fn new() -> io::Result { #[cfg(not(target_os = "espidf"))] let flags = libc::EFD_CLOEXEC | libc::EFD_NONBLOCK; // ESP-IDF is EFD_NONBLOCK by default and errors if you try to pass this flag. #[cfg(target_os = "espidf")] let flags = 0; let fd = syscall!(eventfd(0, flags))?; let file = unsafe { File::from_raw_fd(fd) }; Ok(WakerInternal { fd: file }) } pub fn wake(&self) -> io::Result<()> { let buf: [u8; 8] = 1u64.to_ne_bytes(); match (&self.fd).write(&buf) { Ok(_) => Ok(()), Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { // Writing only blocks if the counter is going to overflow. // So we'll reset the counter to 0 and wake it again. self.reset()?; self.wake() } Err(err) => Err(err), } } #[cfg(mio_unsupported_force_poll_poll)] pub fn ack_and_reset(&self) { let _ = self.reset(); } /// Reset the eventfd object, only need to call this if `wake` fails. fn reset(&self) -> io::Result<()> { let mut buf: [u8; 8] = 0u64.to_ne_bytes(); match (&self.fd).read(&mut buf) { Ok(_) => Ok(()), // If the `Waker` hasn't been awoken yet this will return a // `WouldBlock` error which we can safely ignore. Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Ok(()), Err(err) => Err(err), } } } impl AsRawFd for WakerInternal { fn as_raw_fd(&self) -> RawFd { self.fd.as_raw_fd() } } } #[cfg(all( mio_unsupported_force_poll_poll, not(mio_unsupported_force_waker_pipe), any(target_os = "linux", target_os = "android", target_os = "espidf") ))] pub(crate) use self::eventfd::WakerInternal; #[cfg(all( not(mio_unsupported_force_waker_pipe), any( target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "tvos", target_os = "watchos", ) ))] mod kqueue { use crate::sys::Selector; use crate::Token; use std::io; /// Waker backed by kqueue user space notifications (`EVFILT_USER`). /// /// The implementation is fairly simple, first the kqueue must be setup to /// receive waker events this done by calling `Selector.setup_waker`. Next /// we need access to kqueue, thus we need to duplicate the file descriptor. /// Now waking is as simple as adding an event to the kqueue. #[derive(Debug)] pub struct Waker { selector: Selector, token: Token, } impl Waker { pub fn new(selector: &Selector, token: Token) -> io::Result { let selector = selector.try_clone()?; selector.setup_waker(token)?; Ok(Waker { selector, token }) } pub fn wake(&self) -> io::Result<()> { self.selector.wake(self.token) } } } #[cfg(all( not(mio_unsupported_force_waker_pipe), any( target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "tvos", target_os = "watchos", ) ))] pub use self::kqueue::Waker; #[cfg(any( mio_unsupported_force_waker_pipe, target_os = "aix", target_os = "dragonfly", target_os = "illumos", target_os = "netbsd", target_os = "openbsd", target_os = "redox", target_os = "solaris", target_os = "vita", ))] mod pipe { use crate::sys::unix::pipe; use std::fs::File; use std::io::{self, Read, Write}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; /// Waker backed by a unix pipe. /// /// Waker controls both the sending and receiving ends and empties the pipe /// if writing to it (waking) fails. #[derive(Debug)] pub struct WakerInternal { sender: File, receiver: File, } impl WakerInternal { pub fn new() -> io::Result { let [receiver, sender] = pipe::new_raw()?; let sender = unsafe { File::from_raw_fd(sender) }; let receiver = unsafe { File::from_raw_fd(receiver) }; Ok(WakerInternal { sender, receiver }) } pub fn wake(&self) -> io::Result<()> { // The epoll emulation on some illumos systems currently requires // the pipe buffer to be completely empty for an edge-triggered // wakeup on the pipe read side. #[cfg(target_os = "illumos")] self.empty(); match (&self.sender).write(&[1]) { Ok(_) => Ok(()), Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { // The reading end is full so we'll empty the buffer and try // again. self.empty(); self.wake() } Err(ref err) if err.kind() == io::ErrorKind::Interrupted => self.wake(), Err(err) => Err(err), } } #[cfg(any( mio_unsupported_force_poll_poll, target_os = "solaris", target_os = "vita" ))] pub fn ack_and_reset(&self) { self.empty(); } /// Empty the pipe's buffer, only need to call this if `wake` fails. /// This ignores any errors. fn empty(&self) { let mut buf = [0; 4096]; loop { match (&self.receiver).read(&mut buf) { Ok(n) if n > 0 => continue, _ => return, } } } } impl AsRawFd for WakerInternal { fn as_raw_fd(&self) -> RawFd { self.receiver.as_raw_fd() } } } #[cfg(any( all( mio_unsupported_force_poll_poll, any( mio_unsupported_force_waker_pipe, target_os = "aix", target_os = "dragonfly", target_os = "illumos", target_os = "netbsd", target_os = "openbsd", target_os = "redox", ) ), target_os = "solaris", target_os = "vita", ))] pub(crate) use self::pipe::WakerInternal; #[cfg(any( mio_unsupported_force_poll_poll, target_os = "solaris", target_os = "vita" ))] mod poll { use crate::sys::Selector; use crate::Token; use std::io; #[derive(Debug)] pub struct Waker { selector: Selector, token: Token, } impl Waker { pub fn new(selector: &Selector, token: Token) -> io::Result { Ok(Waker { selector: selector.try_clone()?, token, }) } pub fn wake(&self) -> io::Result<()> { self.selector.wake(self.token) } } } #[cfg(any( mio_unsupported_force_poll_poll, target_os = "solaris", target_os = "vita" ))] pub use self::poll::Waker;