1use crate::{sys, Token};
2
3use 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)]
17pub struct Event {
18 inner: sys::Event,
19}
20
21impl Event {
22 /// Returns the event's token.
23 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 must
32 /// application 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.
37 pub fn is_readable(&self) -> bool {
38 sys::event::is_readable(&self.inner)
39 }
40
41 /// Returns true if the event contains writable readiness.
42 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
67 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
98 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
128 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]
150 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
172 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.
182 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.
187 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
202impl fmt::Debug for Event {
203 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