1 | //! Linux `epoll` support. |
2 | //! |
3 | //! # Examples |
4 | //! |
5 | //! ```no_run |
6 | //! # #[cfg (feature = "net" )] |
7 | //! # fn main() -> std::io::Result<()> { |
8 | //! use rustix::event::epoll; |
9 | //! use rustix::fd::AsFd; |
10 | //! use rustix::io::{ioctl_fionbio, read, write}; |
11 | //! use rustix::net::{ |
12 | //! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, SocketAddrV4, SocketType, |
13 | //! }; |
14 | //! use std::collections::HashMap; |
15 | //! use std::os::unix::io::AsRawFd; |
16 | //! |
17 | //! // Create a socket and listen on it. |
18 | //! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, None)?; |
19 | //! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?; |
20 | //! listen(&listen_sock, 1)?; |
21 | //! |
22 | //! // Create an epoll object. Using `Owning` here means the epoll object will |
23 | //! // take ownership of the file descriptors registered with it. |
24 | //! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?; |
25 | //! |
26 | //! // Register the socket with the epoll object. |
27 | //! epoll::add( |
28 | //! &epoll, |
29 | //! &listen_sock, |
30 | //! epoll::EventData::new_u64(1), |
31 | //! epoll::EventFlags::IN, |
32 | //! )?; |
33 | //! |
34 | //! // Keep track of the sockets we've opened. |
35 | //! let mut next_id = epoll::EventData::new_u64(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::wait(&epoll, &mut event_list, -1)?; |
42 | //! for event in &event_list { |
43 | //! let target = event.data; |
44 | //! if target.u64() == 1 { |
45 | //! // Accept a new connection, set it to non-blocking, and |
46 | //! // register to be notified when it's ready to write to. |
47 | //! let conn_sock = accept(&listen_sock)?; |
48 | //! ioctl_fionbio(&conn_sock, true)?; |
49 | //! epoll::add( |
50 | //! &epoll, |
51 | //! &conn_sock, |
52 | //! next_id, |
53 | //! epoll::EventFlags::OUT | epoll::EventFlags::ET, |
54 | //! )?; |
55 | //! |
56 | //! // Keep track of the socket. |
57 | //! sockets.insert(next_id, conn_sock); |
58 | //! next_id = epoll::EventData::new_u64(next_id.u64() + 1); |
59 | //! } else { |
60 | //! // Write a message to the stream and then unregister it. |
61 | //! let target = sockets.remove(&target).unwrap(); |
62 | //! write(&target, b"hello \n" )?; |
63 | //! let _ = epoll::delete(&epoll, &target)?; |
64 | //! } |
65 | //! } |
66 | //! } |
67 | //! # } |
68 | //! # #[cfg (not(feature = "net" ))] |
69 | //! # fn main() {} |
70 | //! ``` |
71 | |
72 | #![allow (unsafe_code)] |
73 | |
74 | use crate::backend::c; |
75 | use crate::backend::event::syscalls; |
76 | use crate::fd::{AsFd, AsRawFd, OwnedFd}; |
77 | use crate::io; |
78 | #[cfg (feature = "alloc" )] |
79 | use alloc::vec::Vec; |
80 | use bitflags::bitflags; |
81 | use core::ffi::c_void; |
82 | use core::hash::{Hash, Hasher}; |
83 | use core::slice; |
84 | |
85 | bitflags! { |
86 | /// `EPOLL_*` for use with [`new`]. |
87 | #[repr (transparent)] |
88 | #[derive (Copy, Clone, Eq, PartialEq, Hash, Debug)] |
89 | pub struct CreateFlags: c::c_uint { |
90 | /// `EPOLL_CLOEXEC` |
91 | const CLOEXEC = linux_raw_sys::general::EPOLL_CLOEXEC; |
92 | |
93 | /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> |
94 | const _ = !0; |
95 | } |
96 | } |
97 | |
98 | bitflags! { |
99 | /// `EPOLL*` for use with [`add`]. |
100 | #[repr (transparent)] |
101 | #[derive (Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] |
102 | pub struct EventFlags: u32 { |
103 | /// `EPOLLIN` |
104 | const IN = linux_raw_sys::general::EPOLLIN as u32; |
105 | |
106 | /// `EPOLLOUT` |
107 | const OUT = linux_raw_sys::general::EPOLLOUT as u32; |
108 | |
109 | /// `EPOLLPRI` |
110 | const PRI = linux_raw_sys::general::EPOLLPRI as u32; |
111 | |
112 | /// `EPOLLERR` |
113 | const ERR = linux_raw_sys::general::EPOLLERR as u32; |
114 | |
115 | /// `EPOLLHUP` |
116 | const HUP = linux_raw_sys::general::EPOLLHUP as u32; |
117 | |
118 | /// `EPOLLRDNORM` |
119 | const RDNORM = linux_raw_sys::general::EPOLLRDNORM as u32; |
120 | |
121 | /// `EPOLLRDBAND` |
122 | const RDBAND = linux_raw_sys::general::EPOLLRDBAND as u32; |
123 | |
124 | /// `EPOLLWRNORM` |
125 | const WRNORM = linux_raw_sys::general::EPOLLWRNORM as u32; |
126 | |
127 | /// `EPOLLWRBAND` |
128 | const WRBAND = linux_raw_sys::general::EPOLLWRBAND as u32; |
129 | |
130 | /// `EPOLLMSG` |
131 | const MSG = linux_raw_sys::general::EPOLLMSG as u32; |
132 | |
133 | /// `EPOLLRDHUP` |
134 | const RDHUP = linux_raw_sys::general::EPOLLRDHUP as u32; |
135 | |
136 | /// `EPOLLET` |
137 | const ET = linux_raw_sys::general::EPOLLET as u32; |
138 | |
139 | /// `EPOLLONESHOT` |
140 | const ONESHOT = linux_raw_sys::general::EPOLLONESHOT as u32; |
141 | |
142 | /// `EPOLLWAKEUP` |
143 | const WAKEUP = linux_raw_sys::general::EPOLLWAKEUP as u32; |
144 | |
145 | /// `EPOLLEXCLUSIVE` |
146 | const EXCLUSIVE = linux_raw_sys::general::EPOLLEXCLUSIVE as u32; |
147 | |
148 | /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> |
149 | const _ = !0; |
150 | } |
151 | } |
152 | |
153 | /// `epoll_create1(flags)`—Creates a new epoll object. |
154 | /// |
155 | /// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file |
156 | /// descriptor from being implicitly passed across `exec` boundaries. |
157 | #[inline ] |
158 | #[doc (alias = "epoll_create1" )] |
159 | pub fn create(flags: CreateFlags) -> io::Result<OwnedFd> { |
160 | syscalls::epoll_create(flags) |
161 | } |
162 | |
163 | /// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll |
164 | /// object. |
165 | /// |
166 | /// This registers interest in any of the events set in `events` occurring on |
167 | /// the file descriptor associated with `data`. |
168 | /// |
169 | /// If [`delete`] is not called on the I/O source passed into this function |
170 | /// before the I/O source is `close`d, then the `epoll` will act as if the I/O |
171 | /// source is still registered with it. This can lead to spurious events being |
172 | /// returned from [`wait`]. If a file descriptor is an |
173 | /// `Arc<dyn SystemResource>`, then `epoll` can be thought to maintain a |
174 | /// `Weak<dyn SystemResource>` to the file descriptor. |
175 | #[doc (alias = "epoll_ctl" )] |
176 | #[inline ] |
177 | pub fn add( |
178 | epoll: impl AsFd, |
179 | source: impl AsFd, |
180 | data: EventData, |
181 | event_flags: EventFlags, |
182 | ) -> io::Result<()> { |
183 | // SAFETY: We're calling `epoll_ctl` via FFI and we know how it |
184 | // behaves. |
185 | unsafe { |
186 | syscalls::epoll_add( |
187 | epfd:epoll.as_fd(), |
188 | source.as_fd().as_raw_fd(), |
189 | &Event { |
190 | flags: event_flags, |
191 | data, |
192 | }, |
193 | ) |
194 | } |
195 | } |
196 | |
197 | /// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in a |
198 | /// given epoll object. |
199 | /// |
200 | /// This sets the events of interest with `target` to `events`. |
201 | #[doc (alias = "epoll_ctl" )] |
202 | #[inline ] |
203 | pub fn modify( |
204 | epoll: impl AsFd, |
205 | source: impl AsFd, |
206 | data: EventData, |
207 | event_flags: EventFlags, |
208 | ) -> io::Result<()> { |
209 | // SAFETY: We're calling `epoll_ctl` via FFI and we know how it |
210 | // behaves. |
211 | unsafe { |
212 | let raw_fd: i32 = source.as_fd().as_raw_fd(); |
213 | syscalls::epoll_mod( |
214 | epfd:epoll.as_fd(), |
215 | raw_fd, |
216 | &Event { |
217 | flags: event_flags, |
218 | data, |
219 | }, |
220 | ) |
221 | } |
222 | } |
223 | |
224 | /// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a |
225 | /// given epoll object. |
226 | #[doc (alias = "epoll_ctl" )] |
227 | #[inline ] |
228 | pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { |
229 | // SAFETY: We're calling `epoll_ctl` via FFI and we know how it |
230 | // behaves. |
231 | unsafe { |
232 | let raw_fd: i32 = source.as_fd().as_raw_fd(); |
233 | syscalls::epoll_del(epfd:epoll.as_fd(), raw_fd) |
234 | } |
235 | } |
236 | |
237 | /// `epoll_wait(self, events, timeout)`—Waits for registered events of |
238 | /// interest. |
239 | /// |
240 | /// For each event of interest, an element is written to `events`. On |
241 | /// success, this returns the number of written elements. |
242 | #[cfg (feature = "alloc" )] |
243 | #[cfg_attr (doc_cfg, doc(cfg(feature = "alloc" )))] |
244 | #[inline ] |
245 | pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { |
246 | // SAFETY: We're calling `epoll_wait` via FFI and we know how it |
247 | // behaves. |
248 | unsafe { |
249 | event_list.events.set_len(new_len:0); |
250 | let nfds: usize = syscalls::epoll_wait( |
251 | epfd:epoll.as_fd(), |
252 | events:event_list.events[..].as_mut_ptr().cast(), |
253 | num_events:event_list.events.capacity(), |
254 | timeout, |
255 | )?; |
256 | event_list.events.set_len(new_len:nfds); |
257 | } |
258 | |
259 | Ok(()) |
260 | } |
261 | |
262 | /// An iterator over the `Event`s in an `EventVec`. |
263 | pub struct Iter<'a> { |
264 | /// Use `Copied` to copy the struct, since `Event` is `packed` on some |
265 | /// platforms, and it's common for users to directly destructure it, which |
266 | /// would lead to errors about forming references to packed fields. |
267 | iter: core::iter::Copied<slice::Iter<'a, Event>>, |
268 | } |
269 | |
270 | impl<'a> Iterator for Iter<'a> { |
271 | type Item = Event; |
272 | |
273 | #[inline ] |
274 | fn next(&mut self) -> Option<Self::Item> { |
275 | self.iter.next() |
276 | } |
277 | } |
278 | |
279 | /// A record of an event that occurred. |
280 | #[repr (C)] |
281 | #[cfg_attr (target_arch = "x86_64" , repr(packed))] |
282 | #[derive (Copy, Clone, Eq, PartialEq, Hash)] |
283 | pub struct Event { |
284 | /// Which specific event(s) occurred. |
285 | pub flags: EventFlags, |
286 | /// User data. |
287 | pub data: EventData, |
288 | } |
289 | |
290 | /// Data associated with an [`Event`]. This can either be a 64-bit integer |
291 | /// value or a pointer which preserves pointer provenance. |
292 | #[repr (C)] |
293 | #[derive (Copy, Clone)] |
294 | pub union EventData { |
295 | /// A 64-bit integer value. |
296 | as_u64: u64, |
297 | |
298 | /// A `*mut c_void` which preserves pointer provenance, extended to be |
299 | /// 64-bit so that if we read the value as a `u64` union field, we don't |
300 | /// get uninitialized memory. |
301 | sixty_four_bit_pointer: SixtyFourBitPointer, |
302 | } |
303 | |
304 | impl EventData { |
305 | /// Construct a new value containing a `u64`. |
306 | #[inline ] |
307 | pub const fn new_u64(value: u64) -> Self { |
308 | Self { as_u64: value } |
309 | } |
310 | |
311 | /// Construct a new value containing a `*mut c_void`. |
312 | #[inline ] |
313 | pub const fn new_ptr(value: *mut c_void) -> Self { |
314 | Self { |
315 | sixty_four_bit_pointer: SixtyFourBitPointer { |
316 | pointer: value, |
317 | #[cfg (target_pointer_width = "32" )] |
318 | _padding: 0, |
319 | }, |
320 | } |
321 | } |
322 | |
323 | /// Return the value as a `u64`. |
324 | /// |
325 | /// If the stored value was a pointer, the pointer is zero-extended to a |
326 | /// `u64`. |
327 | #[inline ] |
328 | pub fn u64(self) -> u64 { |
329 | unsafe { self.as_u64 } |
330 | } |
331 | |
332 | /// Return the value as a `*mut c_void`. |
333 | /// |
334 | /// If the stored value was a `u64`, the least-significant bits of the |
335 | /// `u64` are returned as a pointer value. |
336 | #[inline ] |
337 | pub fn ptr(self) -> *mut c_void { |
338 | unsafe { self.sixty_four_bit_pointer.pointer } |
339 | } |
340 | } |
341 | |
342 | impl PartialEq for EventData { |
343 | #[inline ] |
344 | fn eq(&self, other: &Self) -> bool { |
345 | self.u64() == other.u64() |
346 | } |
347 | } |
348 | |
349 | impl Eq for EventData {} |
350 | |
351 | impl Hash for EventData { |
352 | #[inline ] |
353 | fn hash<H: Hasher>(&self, state: &mut H) { |
354 | self.u64().hash(state) |
355 | } |
356 | } |
357 | |
358 | #[repr (C)] |
359 | #[derive (Copy, Clone)] |
360 | struct SixtyFourBitPointer { |
361 | #[cfg (target_endian = "big" )] |
362 | #[cfg (target_pointer_width = "32" )] |
363 | _padding: u32, |
364 | |
365 | pointer: *mut c_void, |
366 | |
367 | #[cfg (target_endian = "little" )] |
368 | #[cfg (target_pointer_width = "32" )] |
369 | _padding: u32, |
370 | } |
371 | |
372 | /// A vector of `Event`s, plus context for interpreting them. |
373 | #[cfg (feature = "alloc" )] |
374 | pub struct EventVec { |
375 | events: Vec<Event>, |
376 | } |
377 | |
378 | #[cfg (feature = "alloc" )] |
379 | impl EventVec { |
380 | /// Constructs an `EventVec` from raw pointer, length, and capacity. |
381 | /// |
382 | /// # Safety |
383 | /// |
384 | /// This function calls [`Vec::from_raw_parts`] with its arguments. |
385 | /// |
386 | /// [`Vec::from_raw_parts`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts |
387 | #[inline ] |
388 | pub unsafe fn from_raw_parts(ptr: *mut Event, len: usize, capacity: usize) -> Self { |
389 | Self { |
390 | events: Vec::from_raw_parts(ptr, len, capacity), |
391 | } |
392 | } |
393 | |
394 | /// Constructs an `EventVec` with memory for `capacity` `Event`s. |
395 | #[inline ] |
396 | pub fn with_capacity(capacity: usize) -> Self { |
397 | Self { |
398 | events: Vec::with_capacity(capacity), |
399 | } |
400 | } |
401 | |
402 | /// Returns the current `Event` capacity of this `EventVec`. |
403 | #[inline ] |
404 | pub fn capacity(&self) -> usize { |
405 | self.events.capacity() |
406 | } |
407 | |
408 | /// Reserves enough memory for at least `additional` more `Event`s. |
409 | #[inline ] |
410 | pub fn reserve(&mut self, additional: usize) { |
411 | self.events.reserve(additional); |
412 | } |
413 | |
414 | /// Reserves enough memory for exactly `additional` more `Event`s. |
415 | #[inline ] |
416 | pub fn reserve_exact(&mut self, additional: usize) { |
417 | self.events.reserve_exact(additional); |
418 | } |
419 | |
420 | /// Clears all the `Events` out of this `EventVec`. |
421 | #[inline ] |
422 | pub fn clear(&mut self) { |
423 | self.events.clear(); |
424 | } |
425 | |
426 | /// Shrinks the capacity of this `EventVec` as much as possible. |
427 | #[inline ] |
428 | pub fn shrink_to_fit(&mut self) { |
429 | self.events.shrink_to_fit(); |
430 | } |
431 | |
432 | /// Returns an iterator over the `Event`s in this `EventVec`. |
433 | #[inline ] |
434 | pub fn iter(&self) -> Iter<'_> { |
435 | Iter { |
436 | iter: self.events.iter().copied(), |
437 | } |
438 | } |
439 | |
440 | /// Returns the number of `Event`s logically contained in this `EventVec`. |
441 | #[inline ] |
442 | pub fn len(&mut self) -> usize { |
443 | self.events.len() |
444 | } |
445 | |
446 | /// Tests whether this `EventVec` is logically empty. |
447 | #[inline ] |
448 | pub fn is_empty(&mut self) -> bool { |
449 | self.events.is_empty() |
450 | } |
451 | } |
452 | |
453 | #[cfg (feature = "alloc" )] |
454 | impl<'a> IntoIterator for &'a EventVec { |
455 | type IntoIter = Iter<'a>; |
456 | type Item = Event; |
457 | |
458 | #[inline ] |
459 | fn into_iter(self) -> Self::IntoIter { |
460 | self.iter() |
461 | } |
462 | } |
463 | |
464 | #[test ] |
465 | fn test_epoll_layouts() { |
466 | check_renamed_type!(Event, epoll_event); |
467 | check_renamed_type!(Event, epoll_event); |
468 | check_renamed_struct_renamed_field!(Event, epoll_event, flags, events); |
469 | check_renamed_struct_renamed_field!(Event, epoll_event, data, data); |
470 | } |
471 | |