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}