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. |
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 | /// Reset the eventfd object, only need to call this if `wake` fails. |
71 | #[allow (clippy::unused_io_amount)] // Don't care about partial reads. |
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 { |
85 | fn as_raw_fd(&self) -> RawFd { |
86 | self.fd.as_raw_fd() |
87 | } |
88 | } |
89 | |