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. |
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 |
202 | impl 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 | |