1 | use crate::{Interest, Token}; |
2 | |
3 | use libc::{EPOLLET, EPOLLIN, EPOLLOUT, EPOLLPRI, EPOLLRDHUP}; |
4 | use std::os::unix::io::{AsRawFd, RawFd}; |
5 | #[cfg (debug_assertions)] |
6 | use std::sync::atomic::{AtomicUsize, Ordering}; |
7 | use std::time::Duration; |
8 | use std::{cmp, i32, io, ptr}; |
9 | |
10 | /// Unique id for use as `SelectorId`. |
11 | #[cfg (debug_assertions)] |
12 | static NEXT_ID: AtomicUsize = AtomicUsize::new(1); |
13 | |
14 | #[derive (Debug)] |
15 | pub struct Selector { |
16 | #[cfg (debug_assertions)] |
17 | id: usize, |
18 | ep: RawFd, |
19 | } |
20 | |
21 | impl Selector { |
22 | pub fn new() -> io::Result<Selector> { |
23 | #[cfg (not(target_os = "android" ))] |
24 | let res = syscall!(epoll_create1(libc::EPOLL_CLOEXEC)); |
25 | |
26 | // On Android < API level 16 `epoll_create1` is not defined, so use a |
27 | // raw system call. |
28 | // According to libuv, `EPOLL_CLOEXEC` is not defined on Android API < |
29 | // 21. But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on that platform, |
30 | // so we use it instead. |
31 | #[cfg (target_os = "android" )] |
32 | let res = syscall!(syscall(libc::SYS_epoll_create1, libc::O_CLOEXEC)); |
33 | |
34 | let ep = match res { |
35 | Ok(ep) => ep as RawFd, |
36 | Err(err) => { |
37 | // When `epoll_create1` is not available fall back to use |
38 | // `epoll_create` followed by `fcntl`. |
39 | if let Some(libc::ENOSYS) = err.raw_os_error() { |
40 | match syscall!(epoll_create(1024)) { |
41 | Ok(ep) => match syscall!(fcntl(ep, libc::F_SETFD, libc::FD_CLOEXEC)) { |
42 | Ok(ep) => ep as RawFd, |
43 | Err(err) => { |
44 | // `fcntl` failed, cleanup `ep`. |
45 | let _ = unsafe { libc::close(ep) }; |
46 | return Err(err); |
47 | } |
48 | }, |
49 | Err(err) => return Err(err), |
50 | } |
51 | } else { |
52 | return Err(err); |
53 | } |
54 | } |
55 | }; |
56 | |
57 | Ok(Selector { |
58 | #[cfg (debug_assertions)] |
59 | id: NEXT_ID.fetch_add(1, Ordering::Relaxed), |
60 | ep, |
61 | }) |
62 | } |
63 | |
64 | pub fn try_clone(&self) -> io::Result<Selector> { |
65 | syscall!(fcntl(self.ep, libc::F_DUPFD_CLOEXEC, super::LOWEST_FD)).map(|ep| Selector { |
66 | // It's the same selector, so we use the same id. |
67 | #[cfg (debug_assertions)] |
68 | id: self.id, |
69 | ep, |
70 | }) |
71 | } |
72 | |
73 | pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> { |
74 | // A bug in kernels < 2.6.37 makes timeouts larger than LONG_MAX / CONFIG_HZ |
75 | // (approx. 30 minutes with CONFIG_HZ=1200) effectively infinite on 32 bits |
76 | // architectures. The magic number is the same constant used by libuv. |
77 | #[cfg (target_pointer_width = "32" )] |
78 | const MAX_SAFE_TIMEOUT: u128 = 1789569; |
79 | #[cfg (not(target_pointer_width = "32" ))] |
80 | const MAX_SAFE_TIMEOUT: u128 = libc::c_int::max_value() as u128; |
81 | |
82 | let timeout = timeout |
83 | .map(|to| { |
84 | // `Duration::as_millis` truncates, so round up. This avoids |
85 | // turning sub-millisecond timeouts into a zero timeout, unless |
86 | // the caller explicitly requests that by specifying a zero |
87 | // timeout. |
88 | let to_ms = to |
89 | .checked_add(Duration::from_nanos(999_999)) |
90 | .unwrap_or(to) |
91 | .as_millis(); |
92 | cmp::min(MAX_SAFE_TIMEOUT, to_ms) as libc::c_int |
93 | }) |
94 | .unwrap_or(-1); |
95 | |
96 | events.clear(); |
97 | syscall!(epoll_wait( |
98 | self.ep, |
99 | events.as_mut_ptr(), |
100 | events.capacity() as i32, |
101 | timeout, |
102 | )) |
103 | .map(|n_events| { |
104 | // This is safe because `epoll_wait` ensures that `n_events` are |
105 | // assigned. |
106 | unsafe { events.set_len(n_events as usize) }; |
107 | }) |
108 | } |
109 | |
110 | pub fn register(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> { |
111 | let mut event = libc::epoll_event { |
112 | events: interests_to_epoll(interests), |
113 | u64: usize::from(token) as u64, |
114 | #[cfg (target_os = "redox" )] |
115 | _pad: 0, |
116 | }; |
117 | |
118 | syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_ADD, fd, &mut event)).map(|_| ()) |
119 | } |
120 | |
121 | pub fn reregister(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> { |
122 | let mut event = libc::epoll_event { |
123 | events: interests_to_epoll(interests), |
124 | u64: usize::from(token) as u64, |
125 | #[cfg (target_os = "redox" )] |
126 | _pad: 0, |
127 | }; |
128 | |
129 | syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_MOD, fd, &mut event)).map(|_| ()) |
130 | } |
131 | |
132 | pub fn deregister(&self, fd: RawFd) -> io::Result<()> { |
133 | syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_DEL, fd, ptr::null_mut())).map(|_| ()) |
134 | } |
135 | } |
136 | |
137 | cfg_io_source! { |
138 | impl Selector { |
139 | #[cfg (debug_assertions)] |
140 | pub fn id(&self) -> usize { |
141 | self.id |
142 | } |
143 | } |
144 | } |
145 | |
146 | impl AsRawFd for Selector { |
147 | fn as_raw_fd(&self) -> RawFd { |
148 | self.ep |
149 | } |
150 | } |
151 | |
152 | impl Drop for Selector { |
153 | fn drop(&mut self) { |
154 | if let Err(err: Error) = syscall!(close(self.ep)) { |
155 | error!("error closing epoll: {}" , err); |
156 | } |
157 | } |
158 | } |
159 | |
160 | fn interests_to_epoll(interests: Interest) -> u32 { |
161 | let mut kind: i32 = EPOLLET; |
162 | |
163 | if interests.is_readable() { |
164 | kind = kind | EPOLLIN | EPOLLRDHUP; |
165 | } |
166 | |
167 | if interests.is_writable() { |
168 | kind |= EPOLLOUT; |
169 | } |
170 | |
171 | if interests.is_priority() { |
172 | kind |= EPOLLPRI; |
173 | } |
174 | |
175 | kind as u32 |
176 | } |
177 | |
178 | pub type Event = libc::epoll_event; |
179 | pub type Events = Vec<Event>; |
180 | |
181 | pub mod event { |
182 | use std::fmt; |
183 | |
184 | use crate::sys::Event; |
185 | use crate::Token; |
186 | |
187 | pub fn token(event: &Event) -> Token { |
188 | Token(event.u64 as usize) |
189 | } |
190 | |
191 | pub fn is_readable(event: &Event) -> bool { |
192 | (event.events as libc::c_int & libc::EPOLLIN) != 0 |
193 | || (event.events as libc::c_int & libc::EPOLLPRI) != 0 |
194 | } |
195 | |
196 | pub fn is_writable(event: &Event) -> bool { |
197 | (event.events as libc::c_int & libc::EPOLLOUT) != 0 |
198 | } |
199 | |
200 | pub fn is_error(event: &Event) -> bool { |
201 | (event.events as libc::c_int & libc::EPOLLERR) != 0 |
202 | } |
203 | |
204 | pub fn is_read_closed(event: &Event) -> bool { |
205 | // Both halves of the socket have closed |
206 | event.events as libc::c_int & libc::EPOLLHUP != 0 |
207 | // Socket has received FIN or called shutdown(SHUT_RD) |
208 | || (event.events as libc::c_int & libc::EPOLLIN != 0 |
209 | && event.events as libc::c_int & libc::EPOLLRDHUP != 0) |
210 | } |
211 | |
212 | pub fn is_write_closed(event: &Event) -> bool { |
213 | // Both halves of the socket have closed |
214 | event.events as libc::c_int & libc::EPOLLHUP != 0 |
215 | // Unix pipe write end has closed |
216 | || (event.events as libc::c_int & libc::EPOLLOUT != 0 |
217 | && event.events as libc::c_int & libc::EPOLLERR != 0) |
218 | // The other side (read end) of a Unix pipe has closed. |
219 | || event.events as libc::c_int == libc::EPOLLERR |
220 | } |
221 | |
222 | pub fn is_priority(event: &Event) -> bool { |
223 | (event.events as libc::c_int & libc::EPOLLPRI) != 0 |
224 | } |
225 | |
226 | pub fn is_aio(_: &Event) -> bool { |
227 | // Not supported in the kernel, only in libc. |
228 | false |
229 | } |
230 | |
231 | pub fn is_lio(_: &Event) -> bool { |
232 | // Not supported. |
233 | false |
234 | } |
235 | |
236 | pub fn debug_details(f: &mut fmt::Formatter<'_>, event: &Event) -> fmt::Result { |
237 | #[allow (clippy::trivially_copy_pass_by_ref)] |
238 | fn check_events(got: &u32, want: &libc::c_int) -> bool { |
239 | (*got as libc::c_int & want) != 0 |
240 | } |
241 | debug_detail!( |
242 | EventsDetails(u32), |
243 | check_events, |
244 | libc::EPOLLIN, |
245 | libc::EPOLLPRI, |
246 | libc::EPOLLOUT, |
247 | libc::EPOLLRDNORM, |
248 | libc::EPOLLRDBAND, |
249 | libc::EPOLLWRNORM, |
250 | libc::EPOLLWRBAND, |
251 | libc::EPOLLMSG, |
252 | libc::EPOLLERR, |
253 | libc::EPOLLHUP, |
254 | libc::EPOLLET, |
255 | libc::EPOLLRDHUP, |
256 | libc::EPOLLONESHOT, |
257 | #[cfg (target_os = "linux" )] |
258 | libc::EPOLLEXCLUSIVE, |
259 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
260 | libc::EPOLLWAKEUP, |
261 | libc::EPOLL_CLOEXEC, |
262 | ); |
263 | |
264 | // Can't reference fields in packed structures. |
265 | let e_u64 = event.u64; |
266 | f.debug_struct("epoll_event" ) |
267 | .field("events" , &EventsDetails(event.events)) |
268 | .field("u64" , &e_u64) |
269 | .finish() |
270 | } |
271 | } |
272 | |
273 | #[cfg (target_os = "android" )] |
274 | #[test ] |
275 | fn assert_close_on_exec_flag() { |
276 | // This assertion need to be true for Selector::new. |
277 | assert_eq!(libc::O_CLOEXEC, libc::EPOLL_CLOEXEC); |
278 | } |
279 | |