mio/sys/unix/waker/
eventfd.rs

1use std::fs::File;
2use std::io::{self, Read, Write};
3#[cfg(not(target_os = "hermit"))]
4use 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")]
8use std::os::hermit::io::{AsRawFd, FromRawFd, RawFd};
9
10use crate::sys::Selector;
11use 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)]
20pub(crate) struct Waker {
21    fd: File,
22}
23
24impl Waker {
25    #[allow(dead_code)] // Not used by the `poll(2)` implementation.
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
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.
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.
66    pub(crate) fn ack_and_reset(&self) {
67        let _ = self.reset();
68    }
69
70    #[allow(dead_code)] // Only used by the `poll(2)` implementation.
71    pub(crate) fn fd(&self) -> Option<RawFd> {
72        Some(self.as_raw_fd())
73    }
74
75    /// Only ever `true` for the `single_threaded.rs` implementation.
76    #[allow(dead_code)] // Only used by the `poll(2)` implementation.
77    pub(crate) fn woken(&self) -> bool {
78        false
79    }
80
81    /// Reset the eventfd object, only need to call this if `wake` fails.
82    #[allow(clippy::unused_io_amount)] // Don't care about partial reads.
83    fn reset(&self) -> io::Result<()> {
84        let mut buf: [u8; 8] = 0u64.to_ne_bytes();
85        match (&self.fd).read(&mut buf) {
86            Ok(_) => Ok(()),
87            // If the `Waker` hasn't been awoken yet this will return a
88            // `WouldBlock` error which we can safely ignore.
89            Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Ok(()),
90            Err(err) => Err(err),
91        }
92    }
93}
94
95impl AsRawFd for Waker {
96    fn as_raw_fd(&self) -> RawFd {
97        self.fd.as_raw_fd()
98    }
99}