1use crate::{Interest, Token};
2
3use libc::{EPOLLET, EPOLLIN, EPOLLOUT, EPOLLPRI, EPOLLRDHUP};
4use std::os::unix::io::{AsRawFd, RawFd};
5#[cfg(debug_assertions)]
6use std::sync::atomic::{AtomicUsize, Ordering};
7use std::time::Duration;
8use std::{cmp, i32, io, ptr};
9
10/// Unique id for use as `SelectorId`.
11#[cfg(debug_assertions)]
12static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
13
14#[derive(Debug)]
15pub struct Selector {
16 #[cfg(debug_assertions)]
17 id: usize,
18 ep: RawFd,
19}
20
21impl 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
137cfg_io_source! {
138 impl Selector {
139 #[cfg(debug_assertions)]
140 pub fn id(&self) -> usize {
141 self.id
142 }
143 }
144}
145
146impl AsRawFd for Selector {
147 fn as_raw_fd(&self) -> RawFd {
148 self.ep
149 }
150}
151
152impl 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
160fn 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
178pub type Event = libc::epoll_event;
179pub type Events = Vec<Event>;
180
181pub 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]
275fn 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