base/sys/linux/poll.rs
1// Copyright 2017 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::cmp::min;
6use std::fs::File;
7use std::marker::PhantomData;
8use std::mem::MaybeUninit;
9use std::ptr::null_mut;
10use std::time::Duration;
11
12use libc::c_int;
13use libc::epoll_create1;
14use libc::epoll_ctl;
15use libc::epoll_event;
16use libc::epoll_wait;
17use libc::ENOENT;
18use libc::EPOLLHUP;
19use libc::EPOLLIN;
20use libc::EPOLLOUT;
21use libc::EPOLLRDHUP;
22use libc::EPOLL_CLOEXEC;
23use libc::EPOLL_CTL_ADD;
24use libc::EPOLL_CTL_DEL;
25use libc::EPOLL_CTL_MOD;
26use smallvec::SmallVec;
27
28use super::errno_result;
29use super::Result;
30use crate::handle_eintr_errno;
31use crate::AsRawDescriptor;
32use crate::EventToken;
33use crate::EventType;
34use crate::FromRawDescriptor;
35use crate::RawDescriptor;
36use crate::TriggeredEvent;
37
38const EVENT_CONTEXT_MAX_EVENTS: usize = 16;
39
40impl From<EventType> for u32 {
41 fn from(et: EventType) -> u32 {
42 let v = match et {
43 EventType::None => 0,
44 EventType::Read => EPOLLIN,
45 EventType::Write => EPOLLOUT,
46 EventType::ReadWrite => EPOLLIN | EPOLLOUT,
47 };
48 v as u32
49 }
50}
51
52/// Used to poll multiple objects that have file descriptors.
53///
54/// See [`crate::WaitContext`] for an example that uses the cross-platform wrapper.
55pub struct EventContext<T> {
56 epoll_ctx: File,
57 // Needed to satisfy usage of T
58 tokens: PhantomData<[T]>,
59}
60
61impl<T: EventToken> EventContext<T> {
62 /// Creates a new `EventContext`.
63 pub fn new() -> Result<EventContext<T>> {
64 // SAFETY:
65 // Safe because we check the return value.
66 let epoll_fd = unsafe { epoll_create1(EPOLL_CLOEXEC) };
67 if epoll_fd < 0 {
68 return errno_result();
69 }
70 Ok(EventContext {
71 // SAFETY:
72 // Safe because epoll_fd is valid.
73 epoll_ctx: unsafe { File::from_raw_descriptor(epoll_fd) },
74 tokens: PhantomData,
75 })
76 }
77
78 /// Creates a new `EventContext` and adds the slice of `fd` and `token` tuples to the new
79 /// context.
80 ///
81 /// This is equivalent to calling `new` followed by `add_many`. If there is an error, this will
82 /// return the error instead of the new context.
83 pub fn build_with(fd_tokens: &[(&dyn AsRawDescriptor, T)]) -> Result<EventContext<T>> {
84 let ctx = EventContext::new()?;
85 ctx.add_many(fd_tokens)?;
86 Ok(ctx)
87 }
88
89 /// Adds the given slice of `fd` and `token` tuples to this context.
90 ///
91 /// This is equivalent to calling `add` with each `fd` and `token`. If there are any errors,
92 /// this method will stop adding `fd`s and return the first error, leaving this context in a
93 /// undefined state.
94 pub fn add_many(&self, fd_tokens: &[(&dyn AsRawDescriptor, T)]) -> Result<()> {
95 for (fd, token) in fd_tokens {
96 self.add(*fd, T::from_raw_token(token.as_raw_token()))?;
97 }
98 Ok(())
99 }
100
101 /// Adds the given `fd` to this context and associates the given `token` with the `fd`'s
102 /// readable events.
103 ///
104 /// A `fd` can only be added once and does not need to be kept open. If the `fd` is dropped and
105 /// there were no duplicated file descriptors (i.e. adding the same descriptor with a different
106 /// FD number) added to this context, events will not be reported by `wait` anymore.
107 pub fn add(&self, fd: &dyn AsRawDescriptor, token: T) -> Result<()> {
108 self.add_for_event(fd, EventType::Read, token)
109 }
110
111 /// Adds the given `descriptor` to this context, watching for the specified events and
112 /// associates the given 'token' with those events.
113 ///
114 /// A `descriptor` can only be added once and does not need to be kept open. If the `descriptor`
115 /// is dropped and there were no duplicated file descriptors (i.e. adding the same descriptor
116 /// with a different FD number) added to this context, events will not be reported by `wait`
117 /// anymore.
118 pub fn add_for_event(
119 &self,
120 descriptor: &dyn AsRawDescriptor,
121 event_type: EventType,
122 token: T,
123 ) -> Result<()> {
124 let mut evt = epoll_event {
125 events: event_type.into(),
126 u64: token.as_raw_token(),
127 };
128 // SAFETY:
129 // Safe because we give a valid epoll FD and FD to watch, as well as a valid epoll_event
130 // structure. Then we check the return value.
131 let ret = unsafe {
132 epoll_ctl(
133 self.epoll_ctx.as_raw_descriptor(),
134 EPOLL_CTL_ADD,
135 descriptor.as_raw_descriptor(),
136 &mut evt,
137 )
138 };
139 if ret < 0 {
140 return errno_result();
141 };
142 Ok(())
143 }
144
145 /// If `fd` was previously added to this context, the watched events will be replaced with
146 /// `event_type` and the token associated with it will be replaced with the given `token`.
147 pub fn modify(&self, fd: &dyn AsRawDescriptor, event_type: EventType, token: T) -> Result<()> {
148 let mut evt = epoll_event {
149 events: event_type.into(),
150 u64: token.as_raw_token(),
151 };
152 // SAFETY:
153 // Safe because we give a valid epoll FD and FD to modify, as well as a valid epoll_event
154 // structure. Then we check the return value.
155 let ret = unsafe {
156 epoll_ctl(
157 self.epoll_ctx.as_raw_descriptor(),
158 EPOLL_CTL_MOD,
159 fd.as_raw_descriptor(),
160 &mut evt,
161 )
162 };
163 if ret < 0 {
164 return errno_result();
165 };
166 Ok(())
167 }
168
169 /// Deletes the given `fd` from this context. If the `fd` is not being polled by this context,
170 /// the call is silently dropped without errors.
171 ///
172 /// If an `fd`'s token shows up in the list of hangup events, it should be removed using this
173 /// method or by closing/dropping (if and only if the fd was never dup()'d/fork()'d) the `fd`.
174 /// Failure to do so will cause the `wait` method to always return immediately, causing ~100%
175 /// CPU load.
176 pub fn delete(&self, fd: &dyn AsRawDescriptor) -> Result<()> {
177 // SAFETY:
178 // Safe because we give a valid epoll FD and FD to stop watching. Then we check the return
179 // value.
180 let ret = unsafe {
181 epoll_ctl(
182 self.epoll_ctx.as_raw_descriptor(),
183 EPOLL_CTL_DEL,
184 fd.as_raw_descriptor(),
185 null_mut(),
186 )
187 };
188 // If epoll_ctl returns ENOENT it means the fd is not part of the current polling set so
189 // there is nothing to delete.
190 if ret < 0 && ret != ENOENT {
191 return errno_result();
192 };
193 Ok(())
194 }
195
196 /// Waits for any events to occur in FDs that were previously added to this context.
197 ///
198 /// The events are level-triggered, meaning that if any events are unhandled (i.e. not reading
199 /// for readable events and not closing for hungup events), subsequent calls to `wait` will
200 /// return immediately. The consequence of not handling an event perpetually while calling
201 /// `wait` is that the callers loop will degenerated to busy loop polling, pinning a CPU to
202 /// ~100% usage.
203 pub fn wait(&self) -> Result<SmallVec<[TriggeredEvent<T>; 16]>> {
204 self.wait_timeout(Duration::new(i64::MAX as u64, 0))
205 }
206
207 /// Like `wait` except will only block for a maximum of the given `timeout`.
208 ///
209 /// This may return earlier than `timeout` with zero events if the duration indicated exceeds
210 /// system limits.
211 pub fn wait_timeout(&self, timeout: Duration) -> Result<SmallVec<[TriggeredEvent<T>; 16]>> {
212 let mut epoll_events: [MaybeUninit<epoll_event>; EVENT_CONTEXT_MAX_EVENTS] =
213 // SAFETY:
214 // `MaybeUnint<T>` has the same layout as plain `T` (`epoll_event` in our case).
215 // We submit an uninitialized array to the `epoll_wait` system call, which returns how many
216 // elements it initialized, and then we convert only the initialized `MaybeUnint` values
217 // into `epoll_event` structures after the call.
218 unsafe { MaybeUninit::uninit().assume_init() };
219
220 let timeout_millis = if timeout.as_secs() as i64 == i64::MAX {
221 // We make the convenient assumption that 2^63 seconds is an effectively unbounded time
222 // frame. This is meant to mesh with `wait` calling us with no timeout.
223 -1
224 } else {
225 // In cases where we the number of milliseconds would overflow an i32, we substitute the
226 // maximum timeout which is ~24.8 days.
227 let millis = timeout
228 .as_secs()
229 .checked_mul(1_000)
230 .and_then(|ms| ms.checked_add(u64::from(timeout.subsec_nanos()) / 1_000_000))
231 .unwrap_or(i32::MAX as u64);
232 min(i32::MAX as u64, millis) as i32
233 };
234 let ret = {
235 let max_events = epoll_events.len() as c_int;
236 // SAFETY:
237 // Safe because we give an epoll context and a properly sized epoll_events array
238 // pointer, which we trust the kernel to fill in properly. The `transmute` is safe,
239 // since `MaybeUnint<T>` has the same layout as `T`, and the `epoll_wait` syscall will
240 // initialize as many elements of the `epoll_events` array as it returns.
241 unsafe {
242 handle_eintr_errno!(epoll_wait(
243 self.epoll_ctx.as_raw_descriptor(),
244 std::mem::transmute(&mut epoll_events[0]),
245 max_events,
246 timeout_millis
247 ))
248 }
249 };
250 if ret < 0 {
251 return errno_result();
252 }
253 let count = ret as usize;
254
255 let events = epoll_events[0..count]
256 .iter()
257 .map(|e| {
258 // SAFETY:
259 // Converting `MaybeUninit<epoll_event>` into `epoll_event` is safe here, since we
260 // are only iterating over elements that the `epoll_wait` system call initialized.
261 let e = unsafe { e.assume_init() };
262 TriggeredEvent {
263 token: T::from_raw_token(e.u64),
264 is_readable: e.events & (EPOLLIN as u32) != 0,
265 is_writable: e.events & (EPOLLOUT as u32) != 0,
266 is_hungup: e.events & ((EPOLLHUP | EPOLLRDHUP) as u32) != 0,
267 }
268 })
269 .collect();
270 Ok(events)
271 }
272}
273
274impl<T: EventToken> AsRawDescriptor for EventContext<T> {
275 fn as_raw_descriptor(&self) -> RawDescriptor {
276 self.epoll_ctx.as_raw_descriptor()
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use std::time::Instant;
283
284 use base_event_token_derive::EventToken;
285
286 use super::*;
287 use crate::Event;
288
289 #[test]
290 fn event_context() {
291 let evt1 = Event::new().unwrap();
292 let evt2 = Event::new().unwrap();
293 evt1.signal().unwrap();
294 evt2.signal().unwrap();
295 let ctx: EventContext<u32> = EventContext::build_with(&[(&evt1, 1), (&evt2, 2)]).unwrap();
296
297 let mut evt_count = 0;
298 while evt_count < 2 {
299 for event in ctx.wait().unwrap().iter().filter(|e| e.is_readable) {
300 evt_count += 1;
301 match event.token {
302 1 => {
303 evt1.wait().unwrap();
304 ctx.delete(&evt1).unwrap();
305 }
306 2 => {
307 evt2.wait().unwrap();
308 ctx.delete(&evt2).unwrap();
309 }
310 _ => panic!("unexpected token"),
311 };
312 }
313 }
314 assert_eq!(evt_count, 2);
315 }
316
317 #[test]
318 fn event_context_overflow() {
319 const EVT_COUNT: usize = EVENT_CONTEXT_MAX_EVENTS * 2 + 1;
320 let ctx: EventContext<usize> = EventContext::new().unwrap();
321 let mut evts = Vec::with_capacity(EVT_COUNT);
322 for i in 0..EVT_COUNT {
323 let evt = Event::new().unwrap();
324 evt.signal().unwrap();
325 ctx.add(&evt, i).unwrap();
326 evts.push(evt);
327 }
328 let mut evt_count = 0;
329 while evt_count < EVT_COUNT {
330 for event in ctx.wait().unwrap().iter().filter(|e| e.is_readable) {
331 evts[event.token].wait().unwrap();
332 evt_count += 1;
333 }
334 }
335 }
336
337 #[test]
338 fn event_context_timeout() {
339 let ctx: EventContext<u32> = EventContext::new().unwrap();
340 let dur = Duration::from_millis(10);
341 let start_inst = Instant::now();
342 ctx.wait_timeout(dur).unwrap();
343 assert!(start_inst.elapsed() >= dur);
344 }
345
346 #[test]
347 #[allow(dead_code)]
348 fn event_token_derive() {
349 #[derive(EventToken)]
350 enum EmptyToken {}
351
352 #[derive(PartialEq, Debug, EventToken)]
353 enum Token {
354 Alpha,
355 Beta,
356 // comments
357 Gamma(u32),
358 Delta { index: usize },
359 Omega,
360 }
361
362 assert_eq!(
363 Token::from_raw_token(Token::Alpha.as_raw_token()),
364 Token::Alpha
365 );
366 assert_eq!(
367 Token::from_raw_token(Token::Beta.as_raw_token()),
368 Token::Beta
369 );
370 assert_eq!(
371 Token::from_raw_token(Token::Gamma(55).as_raw_token()),
372 Token::Gamma(55)
373 );
374 assert_eq!(
375 Token::from_raw_token(Token::Delta { index: 100 }.as_raw_token()),
376 Token::Delta { index: 100 }
377 );
378 assert_eq!(
379 Token::from_raw_token(Token::Omega.as_raw_token()),
380 Token::Omega
381 );
382 }
383}