1 | //! Bindings to epoll (Linux, Android).
|
2 |
|
3 | use std::convert::TryInto;
|
4 | use std::io;
|
5 | use std::os::unix::io::{AsRawFd, RawFd};
|
6 | use std::ptr;
|
7 | use std::time::Duration;
|
8 |
|
9 | #[cfg (not(polling_no_io_safety))]
|
10 | use std::os::unix::io::{AsFd, BorrowedFd};
|
11 |
|
12 | use crate::{Event, PollMode};
|
13 |
|
14 | /// Interface to epoll.
|
15 | #[derive (Debug)]
|
16 | pub struct Poller {
|
17 | /// File descriptor for the epoll instance.
|
18 | epoll_fd: RawFd,
|
19 | /// File descriptor for the eventfd that produces notifications.
|
20 | event_fd: RawFd,
|
21 | /// File descriptor for the timerfd that produces timeouts.
|
22 | timer_fd: Option<RawFd>,
|
23 | }
|
24 |
|
25 | impl Poller {
|
26 | /// Creates a new poller.
|
27 | pub fn new() -> io::Result<Poller> {
|
28 | // Create an epoll instance.
|
29 | //
|
30 | // Use `epoll_create1` with `EPOLL_CLOEXEC`.
|
31 | let epoll_fd = syscall!(syscall(
|
32 | libc::SYS_epoll_create1,
|
33 | libc::EPOLL_CLOEXEC as libc::c_int
|
34 | ))
|
35 | .map(|fd| fd as libc::c_int)
|
36 | .or_else(|e| {
|
37 | match e.raw_os_error() {
|
38 | Some(libc::ENOSYS) => {
|
39 | // If `epoll_create1` is not implemented, use `epoll_create`
|
40 | // and manually set `FD_CLOEXEC`.
|
41 | let fd = syscall!(epoll_create(1024))?;
|
42 |
|
43 | if let Ok(flags) = syscall!(fcntl(fd, libc::F_GETFD)) {
|
44 | let _ = syscall!(fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC));
|
45 | }
|
46 |
|
47 | Ok(fd)
|
48 | }
|
49 | _ => Err(e),
|
50 | }
|
51 | })?;
|
52 |
|
53 | // Set up eventfd and timerfd.
|
54 | let event_fd = syscall!(eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK))?;
|
55 | let timer_fd = syscall!(syscall(
|
56 | libc::SYS_timerfd_create,
|
57 | libc::CLOCK_MONOTONIC as libc::c_int,
|
58 | (libc::TFD_CLOEXEC | libc::TFD_NONBLOCK) as libc::c_int,
|
59 | ))
|
60 | .map(|fd| fd as libc::c_int)
|
61 | .ok();
|
62 |
|
63 | let poller = Poller {
|
64 | epoll_fd,
|
65 | event_fd,
|
66 | timer_fd,
|
67 | };
|
68 |
|
69 | if let Some(timer_fd) = timer_fd {
|
70 | poller.add(timer_fd, Event::none(crate::NOTIFY_KEY), PollMode::Oneshot)?;
|
71 | }
|
72 |
|
73 | poller.add(
|
74 | event_fd,
|
75 | Event {
|
76 | key: crate::NOTIFY_KEY,
|
77 | readable: true,
|
78 | writable: false,
|
79 | },
|
80 | PollMode::Oneshot,
|
81 | )?;
|
82 |
|
83 | log::trace!(
|
84 | "new: epoll_fd= {}, event_fd= {}, timer_fd= {:?}" ,
|
85 | epoll_fd,
|
86 | event_fd,
|
87 | timer_fd
|
88 | );
|
89 | Ok(poller)
|
90 | }
|
91 |
|
92 | /// Whether this poller supports level-triggered events.
|
93 | pub fn supports_level(&self) -> bool {
|
94 | true
|
95 | }
|
96 |
|
97 | /// Whether the poller supports edge-triggered events.
|
98 | pub fn supports_edge(&self) -> bool {
|
99 | true
|
100 | }
|
101 |
|
102 | /// Adds a new file descriptor.
|
103 | pub fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> {
|
104 | log::trace!("add: epoll_fd= {}, fd= {}, ev= {:?}" , self.epoll_fd, fd, ev);
|
105 | self.ctl(libc::EPOLL_CTL_ADD, fd, Some((ev, mode)))
|
106 | }
|
107 |
|
108 | /// Modifies an existing file descriptor.
|
109 | pub fn modify(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> {
|
110 | log::trace!("modify: epoll_fd= {}, fd= {}, ev= {:?}" , self.epoll_fd, fd, ev);
|
111 | self.ctl(libc::EPOLL_CTL_MOD, fd, Some((ev, mode)))
|
112 | }
|
113 |
|
114 | /// Deletes a file descriptor.
|
115 | pub fn delete(&self, fd: RawFd) -> io::Result<()> {
|
116 | log::trace!("remove: epoll_fd= {}, fd= {}" , self.epoll_fd, fd);
|
117 | self.ctl(libc::EPOLL_CTL_DEL, fd, None)
|
118 | }
|
119 |
|
120 | /// Waits for I/O events with an optional timeout.
|
121 | pub fn wait(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
|
122 | log::trace!("wait: epoll_fd= {}, timeout= {:?}" , self.epoll_fd, timeout);
|
123 |
|
124 | if let Some(timer_fd) = self.timer_fd {
|
125 | // Configure the timeout using timerfd.
|
126 | let new_val = libc::itimerspec {
|
127 | it_interval: TS_ZERO,
|
128 | it_value: match timeout {
|
129 | None => TS_ZERO,
|
130 | Some(t) => {
|
131 | let mut ts = TS_ZERO;
|
132 | ts.tv_sec = t.as_secs() as libc::time_t;
|
133 | ts.tv_nsec = (t.subsec_nanos() as libc::c_long).into();
|
134 | ts
|
135 | }
|
136 | },
|
137 | };
|
138 |
|
139 | syscall!(timerfd_settime(
|
140 | timer_fd as libc::c_int,
|
141 | 0 as libc::c_int,
|
142 | &new_val as *const libc::itimerspec,
|
143 | ptr::null_mut() as *mut libc::itimerspec
|
144 | ))?;
|
145 |
|
146 | // Set interest in timerfd.
|
147 | self.modify(
|
148 | timer_fd,
|
149 | Event {
|
150 | key: crate::NOTIFY_KEY,
|
151 | readable: true,
|
152 | writable: false,
|
153 | },
|
154 | PollMode::Oneshot,
|
155 | )?;
|
156 | }
|
157 |
|
158 | // Timeout in milliseconds for epoll.
|
159 | let timeout_ms = match (self.timer_fd, timeout) {
|
160 | (_, Some(t)) if t == Duration::from_secs(0) => 0,
|
161 | (None, Some(t)) => {
|
162 | // Round up to a whole millisecond.
|
163 | let mut ms = t.as_millis().try_into().unwrap_or(std::i32::MAX);
|
164 | if Duration::from_millis(ms as u64) < t {
|
165 | ms = ms.saturating_add(1);
|
166 | }
|
167 | ms
|
168 | }
|
169 | _ => -1,
|
170 | };
|
171 |
|
172 | // Wait for I/O events.
|
173 | let res = syscall!(epoll_wait(
|
174 | self.epoll_fd,
|
175 | events.list.as_mut_ptr() as *mut libc::epoll_event,
|
176 | events.list.len() as libc::c_int,
|
177 | timeout_ms as libc::c_int,
|
178 | ))?;
|
179 | events.len = res as usize;
|
180 | log::trace!("new events: epoll_fd= {}, res= {}" , self.epoll_fd, res);
|
181 |
|
182 | // Clear the notification (if received) and re-register interest in it.
|
183 | let mut buf = [0u8; 8];
|
184 | let _ = syscall!(read(
|
185 | self.event_fd,
|
186 | buf.as_mut_ptr() as *mut libc::c_void,
|
187 | buf.len()
|
188 | ));
|
189 | self.modify(
|
190 | self.event_fd,
|
191 | Event {
|
192 | key: crate::NOTIFY_KEY,
|
193 | readable: true,
|
194 | writable: false,
|
195 | },
|
196 | PollMode::Oneshot,
|
197 | )?;
|
198 | Ok(())
|
199 | }
|
200 |
|
201 | /// Sends a notification to wake up the current or next `wait()` call.
|
202 | pub fn notify(&self) -> io::Result<()> {
|
203 | log::trace!(
|
204 | "notify: epoll_fd= {}, event_fd= {}" ,
|
205 | self.epoll_fd,
|
206 | self.event_fd
|
207 | );
|
208 |
|
209 | let buf: [u8; 8] = 1u64.to_ne_bytes();
|
210 | let _ = syscall!(write(
|
211 | self.event_fd,
|
212 | buf.as_ptr() as *const libc::c_void,
|
213 | buf.len()
|
214 | ));
|
215 | Ok(())
|
216 | }
|
217 |
|
218 | /// Passes arguments to `epoll_ctl`.
|
219 | fn ctl(&self, op: libc::c_int, fd: RawFd, ev: Option<(Event, PollMode)>) -> io::Result<()> {
|
220 | let mut ev = ev.map(|(ev, mode)| {
|
221 | let mut flags = match mode {
|
222 | PollMode::Oneshot => libc::EPOLLONESHOT,
|
223 | PollMode::Level => 0,
|
224 | PollMode::Edge => libc::EPOLLET,
|
225 | PollMode::EdgeOneshot => libc::EPOLLONESHOT | libc::EPOLLET,
|
226 | };
|
227 | if ev.readable {
|
228 | flags |= read_flags();
|
229 | }
|
230 | if ev.writable {
|
231 | flags |= write_flags();
|
232 | }
|
233 | libc::epoll_event {
|
234 | events: flags as _,
|
235 | u64: ev.key as u64,
|
236 | }
|
237 | });
|
238 | syscall!(epoll_ctl(
|
239 | self.epoll_fd,
|
240 | op,
|
241 | fd,
|
242 | ev.as_mut()
|
243 | .map(|ev| ev as *mut libc::epoll_event)
|
244 | .unwrap_or(ptr::null_mut()),
|
245 | ))?;
|
246 | Ok(())
|
247 | }
|
248 | }
|
249 |
|
250 | impl AsRawFd for Poller {
|
251 | fn as_raw_fd(&self) -> RawFd {
|
252 | self.epoll_fd
|
253 | }
|
254 | }
|
255 |
|
256 | #[cfg (not(polling_no_io_safety))]
|
257 | impl AsFd for Poller {
|
258 | fn as_fd(&self) -> BorrowedFd<'_> {
|
259 | // SAFETY: lifetime is bound by "self"
|
260 | unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
261 | }
|
262 | }
|
263 |
|
264 | impl Drop for Poller {
|
265 | fn drop(&mut self) {
|
266 | log::trace!(
|
267 | "drop: epoll_fd= {}, event_fd= {}, timer_fd= {:?}" ,
|
268 | self.epoll_fd,
|
269 | self.event_fd,
|
270 | self.timer_fd
|
271 | );
|
272 |
|
273 | if let Some(timer_fd: i32) = self.timer_fd {
|
274 | let _ = self.delete(timer_fd);
|
275 | let _ = syscall!(close(timer_fd));
|
276 | }
|
277 | let _ = self.delete(self.event_fd);
|
278 | let _ = syscall!(close(self.event_fd));
|
279 | let _ = syscall!(close(self.epoll_fd));
|
280 | }
|
281 | }
|
282 |
|
283 | /// `timespec` value that equals zero.
|
284 | const TS_ZERO: libc::timespec =
|
285 | unsafe { std::mem::transmute([0u8; std::mem::size_of::<libc::timespec>()]) };
|
286 |
|
287 | /// Epoll flags for all possible readability events.
|
288 | fn read_flags() -> libc::c_int {
|
289 | libc::EPOLLIN | libc::EPOLLRDHUP | libc::EPOLLHUP | libc::EPOLLERR | libc::EPOLLPRI
|
290 | }
|
291 |
|
292 | /// Epoll flags for all possible writability events.
|
293 | fn write_flags() -> libc::c_int {
|
294 | libc::EPOLLOUT | libc::EPOLLHUP | libc::EPOLLERR
|
295 | }
|
296 |
|
297 | /// A list of reported I/O events.
|
298 | pub struct Events {
|
299 | list: Box<[libc::epoll_event; 1024]>,
|
300 | len: usize,
|
301 | }
|
302 |
|
303 | unsafe impl Send for Events {}
|
304 |
|
305 | impl Events {
|
306 | /// Creates an empty list.
|
307 | pub fn new() -> Events {
|
308 | let ev: epoll_event = libc::epoll_event { events: 0, u64: 0 };
|
309 | let list: Box<[epoll_event; 1024]> = Box::new([ev; 1024]);
|
310 | let len: usize = 0;
|
311 | Events { list, len }
|
312 | }
|
313 |
|
314 | /// Iterates over I/O events.
|
315 | pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
|
316 | self.list[..self.len].iter().map(|ev: &epoll_event| Event {
|
317 | key: ev.u64 as usize,
|
318 | readable: (ev.events as libc::c_int & read_flags()) != 0,
|
319 | writable: (ev.events as libc::c_int & write_flags()) != 0,
|
320 | })
|
321 | }
|
322 | }
|
323 | |