1 | //! epoll support. |
2 | //! |
3 | //! This is an experiment, and it isn't yet clear whether epoll is the right |
4 | //! level of abstraction at which to introduce safety. But it works fairly well |
5 | //! in simple examples 🙂. |
6 | //! |
7 | //! # Examples |
8 | //! |
9 | //! ```no_run |
10 | //! # #![cfg_attr (io_lifetimes_use_std, feature(io_safety))] |
11 | //! # #[cfg (feature = "net" )] |
12 | //! # fn main() -> std::io::Result<()> { |
13 | //! use io_lifetimes::AsFd; |
14 | //! use rustix::io::{epoll, ioctl_fionbio, read, write}; |
15 | //! use rustix::net::{ |
16 | //! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, Protocol, SocketAddrV4, |
17 | //! SocketType, |
18 | //! }; |
19 | //! use std::collections::HashMap; |
20 | //! use std::os::unix::io::AsRawFd; |
21 | //! |
22 | //! // Create a socket and listen on it. |
23 | //! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, Protocol::default())?; |
24 | //! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?; |
25 | //! listen(&listen_sock, 1)?; |
26 | //! |
27 | //! // Create an epoll object. Using `Owning` here means the epoll object will |
28 | //! // take ownership of the file descriptors registered with it. |
29 | //! let epoll = epoll::epoll_create(epoll::CreateFlags::CLOEXEC)?; |
30 | //! |
31 | //! // Register the socket with the epoll object. |
32 | //! epoll::epoll_add(&epoll, &listen_sock, 1, epoll::EventFlags::IN)?; |
33 | //! |
34 | //! // Keep track of the sockets we've opened. |
35 | //! let mut next_id = 2; |
36 | //! let mut sockets = HashMap::new(); |
37 | //! |
38 | //! // Process events. |
39 | //! let mut event_list = epoll::EventVec::with_capacity(4); |
40 | //! loop { |
41 | //! epoll::epoll_wait(&epoll, &mut event_list, -1)?; |
42 | //! for (_event_flags, target) in &event_list { |
43 | //! if target == 1 { |
44 | //! // Accept a new connection, set it to non-blocking, and |
45 | //! // register to be notified when it's ready to write to. |
46 | //! let conn_sock = accept(&listen_sock)?; |
47 | //! ioctl_fionbio(&conn_sock, true)?; |
48 | //! epoll::epoll_add( |
49 | //! &epoll, |
50 | //! &conn_sock, |
51 | //! next_id, |
52 | //! epoll::EventFlags::OUT | epoll::EventFlags::ET, |
53 | //! )?; |
54 | //! |
55 | //! // Keep track of the socket. |
56 | //! sockets.insert(next_id, conn_sock); |
57 | //! next_id += 1; |
58 | //! } else { |
59 | //! // Write a message to the stream and then unregister it. |
60 | //! let target = sockets.remove(&target).unwrap(); |
61 | //! write(&target, b"hello \n" )?; |
62 | //! let _ = epoll::epoll_del(&epoll, &target)?; |
63 | //! } |
64 | //! } |
65 | //! } |
66 | //! # } |
67 | //! # #[cfg (not(feature = "net" ))] |
68 | //! # fn main() {} |
69 | //! ``` |
70 | |
71 | #![allow (unsafe_code)] |
72 | |
73 | use super::super::c; |
74 | use crate::backend::io::syscalls; |
75 | use crate::fd::{AsFd, AsRawFd, OwnedFd}; |
76 | use crate::io; |
77 | use alloc::vec::Vec; |
78 | use bitflags::bitflags; |
79 | use core::slice; |
80 | |
81 | bitflags! { |
82 | /// `EPOLL_*` for use with [`Epoll::new`]. |
83 | pub struct CreateFlags: c::c_uint { |
84 | /// `EPOLL_CLOEXEC` |
85 | const CLOEXEC = linux_raw_sys::general::EPOLL_CLOEXEC; |
86 | } |
87 | } |
88 | |
89 | bitflags! { |
90 | /// `EPOLL*` for use with [`Epoll::add`]. |
91 | #[derive (Default)] |
92 | pub struct EventFlags: u32 { |
93 | /// `EPOLLIN` |
94 | const IN = linux_raw_sys::general::EPOLLIN as u32; |
95 | |
96 | /// `EPOLLOUT` |
97 | const OUT = linux_raw_sys::general::EPOLLOUT as u32; |
98 | |
99 | /// `EPOLLPRI` |
100 | const PRI = linux_raw_sys::general::EPOLLPRI as u32; |
101 | |
102 | /// `EPOLLERR` |
103 | const ERR = linux_raw_sys::general::EPOLLERR as u32; |
104 | |
105 | /// `EPOLLHUP` |
106 | const HUP = linux_raw_sys::general::EPOLLHUP as u32; |
107 | |
108 | /// `EPOLLRDNORM` |
109 | const RDNORM = linux_raw_sys::general::EPOLLRDNORM as u32; |
110 | |
111 | /// `EPOLLRDBAND` |
112 | const RDBAND = linux_raw_sys::general::EPOLLRDBAND as u32; |
113 | |
114 | /// `EPOLLWRNORM` |
115 | const WRNORM = linux_raw_sys::general::EPOLLWRNORM as u32; |
116 | |
117 | /// `EPOLLWRBAND` |
118 | const WRBAND = linux_raw_sys::general::EPOLLWRBAND as u32; |
119 | |
120 | /// `EPOLLMSG` |
121 | const MSG = linux_raw_sys::general::EPOLLMSG as u32; |
122 | |
123 | /// `EPOLLRDHUP` |
124 | const RDHUP = linux_raw_sys::general::EPOLLRDHUP as u32; |
125 | |
126 | /// `EPOLLET` |
127 | const ET = linux_raw_sys::general::EPOLLET as u32; |
128 | |
129 | /// `EPOLLONESHOT` |
130 | const ONESHOT = linux_raw_sys::general::EPOLLONESHOT as u32; |
131 | |
132 | /// `EPOLLWAKEUP` |
133 | const WAKEUP = linux_raw_sys::general::EPOLLWAKEUP as u32; |
134 | |
135 | /// `EPOLLEXCLUSIVE` |
136 | const EXCLUSIVE = linux_raw_sys::general::EPOLLEXCLUSIVE as u32; |
137 | } |
138 | } |
139 | |
140 | /// `epoll_create1(flags)`—Creates a new `Epoll`. |
141 | /// |
142 | /// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file |
143 | /// descriptor from being implicitly passed across `exec` boundaries. |
144 | #[inline ] |
145 | #[doc (alias = "epoll_create1" )] |
146 | pub fn epoll_create(flags: CreateFlags) -> io::Result<OwnedFd> { |
147 | syscalls::epoll_create(flags) |
148 | } |
149 | |
150 | /// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an |
151 | /// `Epoll`. |
152 | /// |
153 | /// This registers interest in any of the events set in `events` occurring |
154 | /// on the file descriptor associated with `data`. |
155 | /// |
156 | /// If `epoll_del` is not called on the I/O source passed into this function |
157 | /// before the I/O source is `close`d, then the `epoll` will act as if the I/O |
158 | /// source is still registered with it. This can lead to spurious events being |
159 | /// returned from `epoll_wait`. If a file descriptor is an |
160 | /// `Arc<dyn SystemResource>`, then `epoll` can be thought to maintain a |
161 | /// `Weak<dyn SystemResource>` to the file descriptor. |
162 | #[doc (alias = "epoll_ctl" )] |
163 | #[inline ] |
164 | pub fn epoll_add( |
165 | epoll: impl AsFd, |
166 | source: impl AsFd, |
167 | data: u64, |
168 | event_flags: EventFlags, |
169 | ) -> io::Result<()> { |
170 | // SAFETY: We're calling `epoll_ctl` via FFI and we know how it |
171 | // behaves. |
172 | unsafe { |
173 | syscalls::epoll_add( |
174 | epfd:epoll.as_fd(), |
175 | source.as_fd().as_raw_fd(), |
176 | &linux_raw_sys::general::epoll_event { |
177 | events: event_flags.bits(), |
178 | data, |
179 | }, |
180 | ) |
181 | } |
182 | } |
183 | |
184 | /// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in |
185 | /// this `Epoll`. |
186 | /// |
187 | /// This sets the events of interest with `target` to `events`. |
188 | #[doc (alias = "epoll_ctl" )] |
189 | #[inline ] |
190 | pub fn epoll_mod( |
191 | epoll: impl AsFd, |
192 | source: impl AsFd, |
193 | data: u64, |
194 | event_flags: EventFlags, |
195 | ) -> io::Result<()> { |
196 | // SAFETY: We're calling `epoll_ctl` via FFI and we know how it |
197 | // behaves. |
198 | unsafe { |
199 | let raw_fd: i32 = source.as_fd().as_raw_fd(); |
200 | syscalls::epoll_mod( |
201 | epfd:epoll.as_fd(), |
202 | raw_fd, |
203 | &linux_raw_sys::general::epoll_event { |
204 | events: event_flags.bits(), |
205 | data, |
206 | }, |
207 | ) |
208 | } |
209 | } |
210 | |
211 | /// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in |
212 | /// this `Epoll`. |
213 | /// |
214 | /// This also returns the owning `Data`. |
215 | #[doc (alias = "epoll_ctl" )] |
216 | #[inline ] |
217 | pub fn epoll_del(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { |
218 | // SAFETY: We're calling `epoll_ctl` via FFI and we know how it |
219 | // behaves. |
220 | unsafe { |
221 | let raw_fd: i32 = source.as_fd().as_raw_fd(); |
222 | syscalls::epoll_del(epfd:epoll.as_fd(), raw_fd) |
223 | } |
224 | } |
225 | |
226 | /// `epoll_wait(self, events, timeout)`—Waits for registered events of |
227 | /// interest. |
228 | /// |
229 | /// For each event of interest, an element is written to `events`. On |
230 | /// success, this returns the number of written elements. |
231 | #[inline ] |
232 | pub fn epoll_wait( |
233 | epoll: impl AsFd, |
234 | event_list: &mut EventVec, |
235 | timeout: c::c_int, |
236 | ) -> io::Result<()> { |
237 | // SAFETY: We're calling `epoll_wait` via FFI and we know how it |
238 | // behaves. |
239 | unsafe { |
240 | event_list.events.set_len(new_len:0); |
241 | let nfds: usize = syscalls::epoll_wait( |
242 | epfd:epoll.as_fd(), |
243 | events:event_list.events[..].as_mut_ptr().cast(), |
244 | num_events:event_list.events.capacity(), |
245 | timeout, |
246 | )?; |
247 | event_list.events.set_len(new_len:nfds); |
248 | } |
249 | |
250 | Ok(()) |
251 | } |
252 | |
253 | /// An iterator over the `Event`s in an `EventVec`. |
254 | pub struct Iter<'a> { |
255 | iter: slice::Iter<'a, Event>, |
256 | } |
257 | |
258 | impl<'a> Iterator for Iter<'a> { |
259 | type Item = (EventFlags, u64); |
260 | |
261 | #[inline ] |
262 | fn next(&mut self) -> Option<Self::Item> { |
263 | self.iter |
264 | .next() |
265 | .map(|event: &Event| (event.event_flags, event.data)) |
266 | } |
267 | } |
268 | |
269 | /// A record of an event that occurred. |
270 | #[repr (C)] |
271 | #[cfg_attr (target_arch = "x86_64" , repr(packed))] |
272 | struct Event { |
273 | // Match the layout of `linux_raw_sys::general::epoll_event`. We just use a |
274 | // `u64` instead of the full union. |
275 | event_flags: EventFlags, |
276 | data: u64, |
277 | } |
278 | |
279 | /// A vector of `Event`s, plus context for interpreting them. |
280 | pub struct EventVec { |
281 | events: Vec<Event>, |
282 | } |
283 | |
284 | impl EventVec { |
285 | /// Constructs an `EventVec` with memory for `capacity` `Event`s. |
286 | #[inline ] |
287 | pub fn with_capacity(capacity: usize) -> Self { |
288 | Self { |
289 | events: Vec::with_capacity(capacity), |
290 | } |
291 | } |
292 | |
293 | /// Returns the current `Event` capacity of this `EventVec`. |
294 | #[inline ] |
295 | pub fn capacity(&self) -> usize { |
296 | self.events.capacity() |
297 | } |
298 | |
299 | /// Reserves enough memory for at least `additional` more `Event`s. |
300 | #[inline ] |
301 | pub fn reserve(&mut self, additional: usize) { |
302 | self.events.reserve(additional); |
303 | } |
304 | |
305 | /// Reserves enough memory for exactly `additional` more `Event`s. |
306 | #[inline ] |
307 | pub fn reserve_exact(&mut self, additional: usize) { |
308 | self.events.reserve_exact(additional); |
309 | } |
310 | |
311 | /// Clears all the `Events` out of this `EventVec`. |
312 | #[inline ] |
313 | pub fn clear(&mut self) { |
314 | self.events.clear(); |
315 | } |
316 | |
317 | /// Shrinks the capacity of this `EventVec` as much as possible. |
318 | #[inline ] |
319 | pub fn shrink_to_fit(&mut self) { |
320 | self.events.shrink_to_fit(); |
321 | } |
322 | |
323 | /// Returns an iterator over the `Event`s in this `EventVec`. |
324 | #[inline ] |
325 | pub fn iter(&self) -> Iter<'_> { |
326 | Iter { |
327 | iter: self.events.iter(), |
328 | } |
329 | } |
330 | |
331 | /// Returns the number of `Event`s logically contained in this `EventVec`. |
332 | #[inline ] |
333 | pub fn len(&mut self) -> usize { |
334 | self.events.len() |
335 | } |
336 | |
337 | /// Tests whether this `EventVec` is logically empty. |
338 | #[inline ] |
339 | pub fn is_empty(&mut self) -> bool { |
340 | self.events.is_empty() |
341 | } |
342 | } |
343 | |
344 | impl<'a> IntoIterator for &'a EventVec { |
345 | type IntoIter = Iter<'a>; |
346 | type Item = (EventFlags, u64); |
347 | |
348 | #[inline ] |
349 | fn into_iter(self) -> Self::IntoIter { |
350 | self.iter() |
351 | } |
352 | } |
353 | |