1//! Bindings to epoll (Linux, Android).
2
3use std::convert::TryInto;
4use std::io;
5use std::os::unix::io::{AsRawFd, RawFd};
6use std::ptr;
7use std::time::Duration;
8
9#[cfg(not(polling_no_io_safety))]
10use std::os::unix::io::{AsFd, BorrowedFd};
11
12use crate::{Event, PollMode};
13
14/// Interface to epoll.
15#[derive(Debug)]
16pub 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
25impl 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
250impl AsRawFd for Poller {
251 fn as_raw_fd(&self) -> RawFd {
252 self.epoll_fd
253 }
254}
255
256#[cfg(not(polling_no_io_safety))]
257impl 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
264impl 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.
284const 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.
288fn 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.
293fn write_flags() -> libc::c_int {
294 libc::EPOLLOUT | libc::EPOLLHUP | libc::EPOLLERR
295}
296
297/// A list of reported I/O events.
298pub struct Events {
299 list: Box<[libc::epoll_event; 1024]>,
300 len: usize,
301}
302
303unsafe impl Send for Events {}
304
305impl 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