1 use crate::{sys, Token};
2 
3 use std::fmt;
4 
5 /// A readiness event.
6 ///
7 /// `Event` is a readiness state paired with a [`Token`]. It is returned by
8 /// [`Poll::poll`].
9 ///
10 /// For more documentation on polling and events, see [`Poll`].
11 ///
12 /// [`Poll::poll`]: ../struct.Poll.html#method.poll
13 /// [`Poll`]: ../struct.Poll.html
14 /// [`Token`]: ../struct.Token.html
15 #[derive(Clone)]
16 #[repr(transparent)]
17 pub struct Event {
18     inner: sys::Event,
19 }
20 
21 impl Event {
22     /// Returns the event's token.
token(&self) -> Token23     pub fn token(&self) -> Token {
24         sys::event::token(&self.inner)
25     }
26 
27     /// Returns true if the event contains readable readiness.
28     ///
29     /// # Notes
30     ///
31     /// Out-of-band (OOB) data also triggers readable events. But most
32     /// applications don't actually read OOB data, this could leave an
33     /// application open to a Denial-of-Service (Dos) attack, see
34     /// <https://github.com/sandstorm-io/sandstorm-website/blob/58f93346028c0576e8147627667328eaaf4be9fa/_posts/2015-04-08-osx-security-bug.md>.
35     /// However because Mio uses edge-triggers it will not result in an infinite
36     /// loop as described in the article above.
is_readable(&self) -> bool37     pub fn is_readable(&self) -> bool {
38         sys::event::is_readable(&self.inner)
39     }
40 
41     /// Returns true if the event contains writable readiness.
is_writable(&self) -> bool42     pub fn is_writable(&self) -> bool {
43         sys::event::is_writable(&self.inner)
44     }
45 
46     /// Returns true if the event contains error readiness.
47     ///
48     /// Error events occur when the socket enters an error state. In this case,
49     /// the socket will also receive a readable or writable event. Reading or
50     /// writing to the socket will result in an error.
51     ///
52     /// # Notes
53     ///
54     /// Method is available on all platforms, but not all platforms trigger the
55     /// error event.
56     ///
57     /// The table below shows what flags are checked on what OS.
58     ///
59     /// | [OS selector] | Flag(s) checked |
60     /// |---------------|-----------------|
61     /// | [epoll]       | `EPOLLERR`      |
62     /// | [kqueue]      | `EV_ERROR` and `EV_EOF` with `fflags` set to `0`. |
63     ///
64     /// [OS selector]: ../struct.Poll.html#implementation-notes
65     /// [epoll]: https://man7.org/linux/man-pages/man7/epoll.7.html
66     /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
is_error(&self) -> bool67     pub fn is_error(&self) -> bool {
68         sys::event::is_error(&self.inner)
69     }
70 
71     /// Returns true if the event contains read closed readiness.
72     ///
73     /// # Notes
74     ///
75     /// Read closed readiness can be expected after any of the following have
76     /// occurred:
77     /// * The local stream has shutdown the read half of its socket
78     /// * The local stream has shutdown both the read half and the write half
79     ///   of its socket
80     /// * The peer stream has shutdown the write half its socket; this sends a
81     ///   `FIN` packet that has been received by the local stream
82     ///
83     /// Method is a best effort implementation. While some platforms may not
84     /// return readiness when read half is closed, it is guaranteed that
85     /// false-positives will not occur.
86     ///
87     /// The table below shows what flags are checked on what OS.
88     ///
89     /// | [OS selector] | Flag(s) checked |
90     /// |---------------|-----------------|
91     /// | [epoll]       | `EPOLLHUP`, or  |
92     /// |               | `EPOLLIN` and `EPOLLRDHUP` |
93     /// | [kqueue]      | `EV_EOF`        |
94     ///
95     /// [OS selector]: ../struct.Poll.html#implementation-notes
96     /// [epoll]: https://man7.org/linux/man-pages/man7/epoll.7.html
97     /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
is_read_closed(&self) -> bool98     pub fn is_read_closed(&self) -> bool {
99         sys::event::is_read_closed(&self.inner)
100     }
101 
102     /// Returns true if the event contains write closed readiness.
103     ///
104     /// # Notes
105     ///
106     /// On [epoll] this is essentially a check for `EPOLLHUP` flag as the
107     /// local stream shutting down its write half does not trigger this event.
108     ///
109     /// On [kqueue] the local stream shutting down the write half of its
110     /// socket will trigger this event.
111     ///
112     /// Method is a best effort implementation. While some platforms may not
113     /// return readiness when write half is closed, it is guaranteed that
114     /// false-positives will not occur.
115     ///
116     /// The table below shows what flags are checked on what OS.
117     ///
118     /// | [OS selector] | Flag(s) checked |
119     /// |---------------|-----------------|
120     /// | [epoll]       | `EPOLLHUP`, or  |
121     /// |               | only `EPOLLERR`, or |
122     /// |               | `EPOLLOUT` and `EPOLLERR` |
123     /// | [kqueue]      | `EV_EOF`        |
124     ///
125     /// [OS selector]: ../struct.Poll.html#implementation-notes
126     /// [epoll]: https://man7.org/linux/man-pages/man7/epoll.7.html
127     /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
is_write_closed(&self) -> bool128     pub fn is_write_closed(&self) -> bool {
129         sys::event::is_write_closed(&self.inner)
130     }
131 
132     /// Returns true if the event contains priority readiness.
133     ///
134     /// # Notes
135     ///
136     /// Method is available on all platforms, but not all platforms trigger the
137     /// priority event.
138     ///
139     /// The table below shows what flags are checked on what OS.
140     ///
141     /// | [OS selector] | Flag(s) checked |
142     /// |---------------|-----------------|
143     /// | [epoll]       | `EPOLLPRI`      |
144     /// | [kqueue]      | *Not supported* |
145     ///
146     /// [OS selector]: ../struct.Poll.html#implementation-notes
147     /// [epoll]: https://man7.org/linux/man-pages/man7/epoll.7.html
148     /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
149     #[inline]
is_priority(&self) -> bool150     pub fn is_priority(&self) -> bool {
151         sys::event::is_priority(&self.inner)
152     }
153 
154     /// Returns true if the event contains AIO readiness.
155     ///
156     /// # Notes
157     ///
158     /// Method is available on all platforms, but not all platforms support AIO.
159     ///
160     /// The table below shows what flags are checked on what OS.
161     ///
162     /// | [OS selector] | Flag(s) checked |
163     /// |---------------|-----------------|
164     /// | [epoll]       | *Not supported* |
165     /// | [kqueue]<sup>1</sup> | `EVFILT_AIO` |
166     ///
167     /// 1: Only supported on DragonFly BSD, FreeBSD, iOS and macOS.
168     ///
169     /// [OS selector]: ../struct.Poll.html#implementation-notes
170     /// [epoll]: https://man7.org/linux/man-pages/man7/epoll.7.html
171     /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
is_aio(&self) -> bool172     pub fn is_aio(&self) -> bool {
173         sys::event::is_aio(&self.inner)
174     }
175 
176     /// Returns true if the event contains LIO readiness.
177     ///
178     /// # Notes
179     ///
180     /// Method is available on all platforms, but only FreeBSD supports LIO. On
181     /// FreeBSD this method checks the `EVFILT_LIO` flag.
is_lio(&self) -> bool182     pub fn is_lio(&self) -> bool {
183         sys::event::is_lio(&self.inner)
184     }
185 
186     /// Create a reference to an `Event` from a platform specific event.
from_sys_event_ref(sys_event: &sys::Event) -> &Event187     pub(crate) fn from_sys_event_ref(sys_event: &sys::Event) -> &Event {
188         unsafe {
189             // This is safe because the memory layout of `Event` is
190             // the same as `sys::Event` due to the `repr(transparent)` attribute.
191             &*(sys_event as *const sys::Event as *const Event)
192         }
193     }
194 }
195 
196 /// When the [alternate] flag is enabled this will print platform specific
197 /// details, for example the fields of the `kevent` structure on platforms that
198 /// use `kqueue(2)`. Note however that the output of this implementation is
199 /// **not** consider a part of the stable API.
200 ///
201 /// [alternate]: fmt::Formatter::alternate
202 impl fmt::Debug for Event {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result203     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204         let alternate = f.alternate();
205         let mut d = f.debug_struct("Event");
206         d.field("token", &self.token())
207             .field("readable", &self.is_readable())
208             .field("writable", &self.is_writable())
209             .field("error", &self.is_error())
210             .field("read_closed", &self.is_read_closed())
211             .field("write_closed", &self.is_write_closed())
212             .field("priority", &self.is_priority())
213             .field("aio", &self.is_aio())
214             .field("lio", &self.is_lio());
215 
216         if alternate {
217             struct EventDetails<'a>(&'a sys::Event);
218 
219             impl<'a> fmt::Debug for EventDetails<'a> {
220                 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221                     sys::event::debug_details(f, self.0)
222                 }
223             }
224 
225             d.field("details", &EventDetails(&self.inner)).finish()
226         } else {
227             d.finish()
228         }
229     }
230 }
231