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