1 // Copyright 2015 Nathan Sizemore <[email protected]>
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4 // If a copy of the MPL was not distributed with this file,
5 // You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 #[macro_use]
8 extern crate bitflags;
9 extern crate libc;
10
11 use std::fmt::{Debug, Formatter, Result};
12 use std::io::{self, Error};
13 use std::os::unix::io::RawFd;
14
15 #[repr(i32)]
16 #[allow(non_camel_case_types)]
17 pub enum ControlOptions {
18 /// Indicates an addition to the interest list.
19 EPOLL_CTL_ADD = libc::EPOLL_CTL_ADD,
20 /// Indicates a modification of flags for an interest already in list.
21 EPOLL_CTL_MOD = libc::EPOLL_CTL_MOD,
22 /// Indicates a removal of an interest from the list.
23 EPOLL_CTL_DEL = libc::EPOLL_CTL_DEL,
24 }
25
26 bitflags! {
27
28 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29 pub struct Events: u32 {
30 /// Sets the Edge Triggered behavior for the associated file descriptor.
31 ///
32 /// The default behavior for epoll is Level Triggered.
33 const EPOLLET = libc::EPOLLET as u32;
34 /// The associated file is available for read operations.
35 const EPOLLIN = libc::EPOLLIN as u32;
36 /// Error condition happened on the associated file descriptor.
37 ///
38 /// `wait` will always wait for this event; is not necessary to set it in events.
39 const EPOLLERR = libc::EPOLLERR as u32;
40 /// Hang up happened on the associated file descriptor.
41 ///
42 /// `wait` will always wait for this event; it is not necessary to set it in events.
43 /// Note that when reading from a channel such as a pipe or a stream socket, this event
44 /// merely indicates that the peer closed its end of the channel. Subsequent reads from
45 /// the channel will return 0 (end of file) only after all outstanding data in the
46 /// channel has been consumed.
47 const EPOLLHUP = libc::EPOLLHUP as u32;
48 /// The associated file is available for write operations.
49 const EPOLLOUT = libc::EPOLLOUT as u32;
50 /// There is urgent data available for read operations.
51 const EPOLLPRI = libc::EPOLLPRI as u32;
52 /// Stream socket peer closed connection, or shut down writing half of connection.
53 ///
54 /// This flag is especially useful for writing simple code to detect peer shutdown when
55 /// using Edge Triggered monitoring.
56 const EPOLLRDHUP = libc::EPOLLRDHUP as u32;
57 /// If `EPOLLONESHOT` and `EPOLLET` are clear and the process has the `CAP_BLOCK_SUSPEND`
58 /// capability, ensure that the system does not enter "suspend" or "hibernate" while this
59 /// event is pending or being processed.
60 ///
61 /// The event is considered as being "processed" from the time when it is returned by
62 /// a call to `wait` until the next call to `wait` on the same `EpollInstance`
63 /// descriptor, the closure of that file descriptor, the removal of the event file
64 /// descriptor with `EPOLL_CTL_DEL`, or the clearing of `EPOLLWAKEUP` for the event file
65 /// descriptor with `EPOLL_CTL_MOD`.
66 const EPOLLWAKEUP = libc::EPOLLWAKEUP as u32;
67 /// Sets the one-shot behavior for the associated file descriptor.
68 ///
69 /// This means that after an event is pulled out with `wait` the associated file
70 /// descriptor is internally disabled and no other events will be reported by the epoll
71 /// interface. The user must call `ctl` with `EPOLL_CTL_MOD` to rearm the file
72 /// descriptor with a new event mask.
73 const EPOLLONESHOT = libc::EPOLLONESHOT as u32;
74 /// Sets an exclusive wakeup mode for the epoll file descriptor that is being attached to
75 /// the target file descriptor, `fd`. When a wakeup event occurs and multiple epoll file
76 /// descriptors are attached to the same target file using `EPOLLEXCLUSIVE`, one or more of
77 /// the epoll file descriptors will receive an event with `wait`. The default in this
78 /// scenario (when `EPOLLEXCLUSIVE` is not set) is for all epoll file descriptors to
79 /// receive an event. `EPOLLEXCLUSIVE` is thus useful for avoiding thundering herd problems
80 /// in certain scenarios.
81 ///
82 /// If the same file descriptor is in multiple epoll instances, some with the
83 /// `EPOLLEXCLUSIVE` flag, and others without, then events will be provided to all epoll
84 /// instances that did not specify `EPOLLEXCLUSIVE`, and at least one of the epoll
85 /// instances that did specify `EPOLLEXCLUSIVE`.
86 ///
87 /// The following values may be specified in conjunction with `EPOLLEXCLUSIVE`: `EPOLLIN`,
88 /// `EPOLLOUT`, `EPOLLWAKEUP`, and `EPOLLET`. `EPOLLHUP` and `EPOLLERR` can also be
89 /// specified, but this is not required: as usual, these events are always reported if they
90 /// occur, regardless of whether they are specified in `Events`. Attempts to specify other
91 /// values in `Events` yield the error `EINVAL`.
92 ///
93 /// `EPOLLEXCLUSIVE` may be used only in an `EPOLL_CTL_ADD` operation; attempts to employ
94 /// it with `EPOLL_CTL_MOD` yield an error. If `EPOLLEXCLUSIVE` has been set using `ctl`,
95 /// then a subsequent `EPOLL_CTL_MOD` on the same `epfd`, `fd` pair yields an error. A call
96 /// to `ctl` that specifies `EPOLLEXCLUSIVE` in `Events` and specifies the target file
97 /// descriptor `fd` as an epoll instance will likewise fail. The error in all of these
98 /// cases is `EINVAL`.
99 ///
100 /// The `EPOLLEXCLUSIVE` flag is an input flag for the `Event.events` field when calling
101 /// `ctl`; it is never returned by `wait`.
102 const EPOLLEXCLUSIVE = libc::EPOLLEXCLUSIVE as u32;
103 }
104 }
105
106 /// 'libc::epoll_event' equivalent.
107 #[repr(C)]
108 #[cfg_attr(target_arch = "x86_64", repr(packed))]
109 #[derive(Clone, Copy)]
110 pub struct Event {
111 pub events: u32,
112 pub data: u64,
113 }
114
115 impl Event {
new(events: Events, data: u64) -> Event116 pub fn new(events: Events, data: u64) -> Event {
117 Event {
118 events: events.bits(),
119 data: data,
120 }
121 }
122 }
123
124 impl Debug for Event {
fmt(&self, f: &mut Formatter<'_>) -> Result125 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
126 let data = self.data;
127 f.debug_struct("Event")
128 .field("events", &Events::from_bits_retain(self.events)) // Retain so we can see erroneously set bits too
129 .field("data", &data)
130 .finish()
131 }
132 }
133
134 /// Creates a new epoll file descriptor.
135 ///
136 /// If `cloexec` is true, `FD_CLOEXEC` will be set on the returned file descriptor.
137 ///
138 /// ## Notes
139 ///
140 /// * `epoll_create1()` is the underlying syscall.
create(cloexec: bool) -> io::Result<RawFd>141 pub fn create(cloexec: bool) -> io::Result<RawFd> {
142 let flags = if cloexec { libc::EPOLL_CLOEXEC } else { 0 };
143 unsafe { cvt(libc::epoll_create1(flags)) }
144 }
145
146 /// Safe wrapper for `libc::epoll_ctl`
ctl( epfd: RawFd, op: ControlOptions, fd: RawFd, mut event: Event, ) -> io::Result<()>147 pub fn ctl(
148 epfd: RawFd,
149 op: ControlOptions,
150 fd: RawFd,
151 mut event: Event,
152 ) -> io::Result<()> {
153 let e = &mut event as *mut _ as *mut libc::epoll_event;
154 unsafe { cvt(libc::epoll_ctl(epfd, op as i32, fd, e))? };
155 Ok(())
156 }
157
158 /// Safe wrapper for `libc::epoll_wait`
159 ///
160 /// ## Notes
161 ///
162 /// * If `timeout` is negative, it will block until an event is received.
wait(epfd: RawFd, timeout: i32, buf: &mut [Event]) -> io::Result<usize>163 pub fn wait(epfd: RawFd, timeout: i32, buf: &mut [Event]) -> io::Result<usize> {
164 let timeout = if timeout < -1 { -1 } else { timeout };
165 let num_events = unsafe {
166 cvt(libc::epoll_wait(
167 epfd,
168 buf.as_mut_ptr() as *mut libc::epoll_event,
169 buf.len() as i32,
170 timeout,
171 ))? as usize
172 };
173 Ok(num_events)
174 }
175
176 /// Safe wrapper for `libc::close`
close(epfd: RawFd) -> io::Result<()>177 pub fn close(epfd: RawFd) -> io::Result<()> {
178 cvt(unsafe { libc::close(epfd) })?;
179 Ok(())
180 }
181
cvt(result: libc::c_int) -> io::Result<libc::c_int>182 fn cvt(result: libc::c_int) -> io::Result<libc::c_int> {
183 if result < 0 {
184 Err(Error::last_os_error())
185 } else {
186 Ok(result)
187 }
188 }
189