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 | #![allow (unused_qualifications)] |
74 | |
75 | use super::epoll; |
76 | #[cfg (feature = "alloc" )] |
77 | use crate::backend::c; |
78 | pub use crate::backend::event::epoll::*; |
79 | use crate::backend::event::syscalls; |
80 | use crate::fd::{AsFd, OwnedFd}; |
81 | use crate::io; |
82 | #[cfg (feature = "alloc" )] |
83 | use alloc::vec::Vec; |
84 | use core::ffi::c_void; |
85 | use core::hash::{Hash, Hasher}; |
86 | use core::slice; |
87 | |
88 | /// `epoll_create1(flags)`—Creates a new epoll object. |
89 | /// |
90 | /// Use the [`epoll::CreateFlags::CLOEXEC`] flag to prevent the resulting file |
91 | /// descriptor from being implicitly passed across `exec` boundaries. |
92 | /// |
93 | /// # References |
94 | /// - [Linux] |
95 | /// - [illumos] |
96 | /// |
97 | /// [Linux]: https://man7.org/linux/man-pages/man2/epoll_create.2.html |
98 | /// [illumos]: https://www.illumos.org/man/3C/epoll_create |
99 | #[inline ] |
100 | #[doc (alias = "epoll_create1" )] |
101 | pub fn create(flags: epoll::CreateFlags) -> io::Result<OwnedFd> { |
102 | syscalls::epoll_create(flags) |
103 | } |
104 | |
105 | /// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll |
106 | /// object. |
107 | /// |
108 | /// This registers interest in any of the events set in `event_flags` occurring |
109 | /// on the file descriptor associated with `data`. |
110 | /// |
111 | /// Note that `close`ing a file descriptor does not necessarily unregister |
112 | /// interest which can lead to spurious events being returned from |
113 | /// [`epoll::wait`]. If a file descriptor is an `Arc<dyn SystemResource>`, then |
114 | /// `epoll` can be thought to maintain a `Weak<dyn SystemResource>` to the file |
115 | /// descriptor. Check the [faq] for details. |
116 | /// |
117 | /// # References |
118 | /// - [Linux] |
119 | /// - [illumos] |
120 | /// |
121 | /// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html |
122 | /// [illumos]: https://www.illumos.org/man/3C/epoll_ctl |
123 | /// [faq]: https://man7.org/linux/man-pages/man7/epoll.7.html#:~:text=Will%20closing%20a%20file%20descriptor%20cause%20it%20to%20be%20removed%20from%20all%0A%20%20%20%20%20%20%20%20%20%20epoll%20interest%20lists%3F |
124 | #[doc (alias = "epoll_ctl" )] |
125 | #[inline ] |
126 | pub fn add( |
127 | epoll: impl AsFd, |
128 | source: impl AsFd, |
129 | data: epoll::EventData, |
130 | event_flags: epoll::EventFlags, |
131 | ) -> io::Result<()> { |
132 | syscalls::epoll_add( |
133 | epfd:epoll.as_fd(), |
134 | source.as_fd(), |
135 | &Event { |
136 | flags: event_flags, |
137 | data, |
138 | #[cfg (all(libc, target_os = "redox" ))] |
139 | _pad: 0, |
140 | }, |
141 | ) |
142 | } |
143 | |
144 | /// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in a |
145 | /// given epoll object. |
146 | /// |
147 | /// This sets the events of interest with `target` to `events`. |
148 | /// |
149 | /// # References |
150 | /// - [Linux] |
151 | /// - [illumos] |
152 | /// |
153 | /// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html |
154 | /// [illumos]: https://www.illumos.org/man/3C/epoll_ctl |
155 | #[doc (alias = "epoll_ctl" )] |
156 | #[inline ] |
157 | pub fn modify( |
158 | epoll: impl AsFd, |
159 | source: impl AsFd, |
160 | data: epoll::EventData, |
161 | event_flags: epoll::EventFlags, |
162 | ) -> io::Result<()> { |
163 | syscalls::epoll_mod( |
164 | epfd:epoll.as_fd(), |
165 | source.as_fd(), |
166 | &Event { |
167 | flags: event_flags, |
168 | data, |
169 | #[cfg (all(libc, target_os = "redox" ))] |
170 | _pad: 0, |
171 | }, |
172 | ) |
173 | } |
174 | |
175 | /// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a |
176 | /// given epoll object. |
177 | /// |
178 | /// # References |
179 | /// - [Linux] |
180 | /// - [illumos] |
181 | /// |
182 | /// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html |
183 | /// [illumos]: https://www.illumos.org/man/3C/epoll_ctl |
184 | #[doc (alias = "epoll_ctl" )] |
185 | #[inline ] |
186 | pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { |
187 | syscalls::epoll_del(epfd:epoll.as_fd(), source.as_fd()) |
188 | } |
189 | |
190 | /// `epoll_wait(self, events, timeout)`—Waits for registered events of |
191 | /// interest. |
192 | /// |
193 | /// For each event of interest, an element is written to `events`. On |
194 | /// success, this returns the number of written elements. |
195 | /// |
196 | /// # References |
197 | /// - [Linux] |
198 | /// - [illumos] |
199 | /// |
200 | /// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html |
201 | /// [illumos]: https://www.illumos.org/man/3C/epoll_wait |
202 | #[cfg (feature = "alloc" )] |
203 | #[cfg_attr (docsrs, doc(cfg(feature = "alloc" ), alias = "epoll_wait" ))] |
204 | #[inline ] |
205 | pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { |
206 | // SAFETY: We're calling `epoll_wait` via FFI and we know how it |
207 | // behaves. |
208 | unsafe { |
209 | event_list.events.clear(); |
210 | let nfds: usize = syscalls::epoll_wait( |
211 | epfd:epoll.as_fd(), |
212 | events:event_list.events.spare_capacity_mut(), |
213 | timeout, |
214 | )?; |
215 | event_list.events.set_len(new_len:nfds); |
216 | } |
217 | |
218 | Ok(()) |
219 | } |
220 | |
221 | /// An iterator over the [`epoll::Event`]s in an [`epoll::EventVec`]. |
222 | pub struct Iter<'a> { |
223 | /// Use `Copied` to copy the struct, since `Event` is `packed` on some |
224 | /// platforms, and it's common for users to directly destructure it, which |
225 | /// would lead to errors about forming references to packed fields. |
226 | iter: core::iter::Copied<slice::Iter<'a, Event>>, |
227 | } |
228 | |
229 | impl<'a> Iterator for Iter<'a> { |
230 | type Item = epoll::Event; |
231 | |
232 | #[inline ] |
233 | fn next(&mut self) -> Option<Self::Item> { |
234 | self.iter.next() |
235 | } |
236 | |
237 | #[inline ] |
238 | fn size_hint(&self) -> (usize, Option<usize>) { |
239 | self.iter.size_hint() |
240 | } |
241 | } |
242 | |
243 | /// A record of an event that occurred. |
244 | #[repr (C)] |
245 | #[cfg_attr (all(not(libc), target_arch = "x86_64" ), repr(packed))] |
246 | #[cfg_attr ( |
247 | all( |
248 | libc, |
249 | linux_kernel, |
250 | any( |
251 | all( |
252 | target_arch = "x86" , |
253 | not(target_env = "musl" ), |
254 | not(target_os = "android" ), |
255 | ), |
256 | target_arch = "x86_64" , |
257 | ) |
258 | ), |
259 | repr(packed) |
260 | )] |
261 | #[derive (Copy, Clone, Eq, PartialEq, Hash)] |
262 | #[cfg_attr ( |
263 | all(solarish, any(target_arch = "x86" , target_arch = "x86_64" )), |
264 | repr(packed(4)) |
265 | )] |
266 | pub struct Event { |
267 | /// Which specific event(s) occurred. |
268 | pub flags: EventFlags, |
269 | /// User data. |
270 | pub data: EventData, |
271 | |
272 | #[cfg (all(libc, target_os = "redox" ))] |
273 | _pad: u64, |
274 | } |
275 | |
276 | /// Data associated with an [`epoll::Event`]. This can either be a 64-bit |
277 | /// integer value or a pointer which preserves pointer provenance. |
278 | #[repr (C)] |
279 | #[derive (Copy, Clone)] |
280 | pub union EventData { |
281 | /// A 64-bit integer value. |
282 | as_u64: u64, |
283 | |
284 | /// A `*mut c_void` which preserves pointer provenance, extended to be |
285 | /// 64-bit so that if we read the value as a `u64` union field, we don't |
286 | /// get uninitialized memory. |
287 | sixty_four_bit_pointer: SixtyFourBitPointer, |
288 | } |
289 | |
290 | impl EventData { |
291 | /// Construct a new value containing a `u64`. |
292 | #[inline ] |
293 | pub const fn new_u64(value: u64) -> Self { |
294 | Self { as_u64: value } |
295 | } |
296 | |
297 | /// Construct a new value containing a `*mut c_void`. |
298 | #[inline ] |
299 | pub const fn new_ptr(value: *mut c_void) -> Self { |
300 | Self { |
301 | sixty_four_bit_pointer: SixtyFourBitPointer { |
302 | pointer: value, |
303 | #[cfg (target_pointer_width = "32" )] |
304 | _padding: 0, |
305 | }, |
306 | } |
307 | } |
308 | |
309 | /// Return the value as a `u64`. |
310 | /// |
311 | /// If the stored value was a pointer, the pointer is zero-extended to a |
312 | /// `u64`. |
313 | #[inline ] |
314 | pub fn u64(self) -> u64 { |
315 | unsafe { self.as_u64 } |
316 | } |
317 | |
318 | /// Return the value as a `*mut c_void`. |
319 | /// |
320 | /// If the stored value was a `u64`, the least-significant bits of the |
321 | /// `u64` are returned as a pointer value. |
322 | #[inline ] |
323 | pub fn ptr(self) -> *mut c_void { |
324 | unsafe { self.sixty_four_bit_pointer.pointer } |
325 | } |
326 | } |
327 | |
328 | impl PartialEq for EventData { |
329 | #[inline ] |
330 | fn eq(&self, other: &Self) -> bool { |
331 | self.u64() == other.u64() |
332 | } |
333 | } |
334 | |
335 | impl Eq for EventData {} |
336 | |
337 | impl Hash for EventData { |
338 | #[inline ] |
339 | fn hash<H: Hasher>(&self, state: &mut H) { |
340 | self.u64().hash(state) |
341 | } |
342 | } |
343 | |
344 | #[repr (C)] |
345 | #[derive (Copy, Clone)] |
346 | struct SixtyFourBitPointer { |
347 | #[cfg (target_endian = "big" )] |
348 | #[cfg (target_pointer_width = "32" )] |
349 | _padding: u32, |
350 | |
351 | pointer: *mut c_void, |
352 | |
353 | #[cfg (target_endian = "little" )] |
354 | #[cfg (target_pointer_width = "32" )] |
355 | _padding: u32, |
356 | } |
357 | |
358 | /// A vector of `epoll::Event`s, plus context for interpreting them. |
359 | #[cfg (feature = "alloc" )] |
360 | pub struct EventVec { |
361 | events: Vec<Event>, |
362 | } |
363 | |
364 | #[cfg (feature = "alloc" )] |
365 | impl EventVec { |
366 | /// Constructs an `epoll::EventVec` from raw pointer, length, and capacity. |
367 | /// |
368 | /// # Safety |
369 | /// |
370 | /// This function calls [`Vec::from_raw_parts`] with its arguments. |
371 | /// |
372 | /// [`Vec::from_raw_parts`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts |
373 | #[inline ] |
374 | pub unsafe fn from_raw_parts(ptr: *mut Event, len: usize, capacity: usize) -> Self { |
375 | Self { |
376 | events: Vec::from_raw_parts(ptr, len, capacity), |
377 | } |
378 | } |
379 | |
380 | /// Constructs an `epoll::EventVec` with memory for `capacity` |
381 | /// `epoll::Event`s. |
382 | #[inline ] |
383 | pub fn with_capacity(capacity: usize) -> Self { |
384 | Self { |
385 | events: Vec::with_capacity(capacity), |
386 | } |
387 | } |
388 | |
389 | /// Returns the current `epoll::Event` capacity of this `epoll::EventVec`. |
390 | #[inline ] |
391 | pub fn capacity(&self) -> usize { |
392 | self.events.capacity() |
393 | } |
394 | |
395 | /// Reserves enough memory for at least `additional` more `epoll::Event`s. |
396 | #[inline ] |
397 | pub fn reserve(&mut self, additional: usize) { |
398 | self.events.reserve(additional); |
399 | } |
400 | |
401 | /// Reserves enough memory for exactly `additional` more `epoll::Event`s. |
402 | #[inline ] |
403 | pub fn reserve_exact(&mut self, additional: usize) { |
404 | self.events.reserve_exact(additional); |
405 | } |
406 | |
407 | /// Clears all the `epoll::Events` out of this `epoll::EventVec`. |
408 | #[inline ] |
409 | pub fn clear(&mut self) { |
410 | self.events.clear(); |
411 | } |
412 | |
413 | /// Shrinks the capacity of this `epoll::EventVec` as much as possible. |
414 | #[inline ] |
415 | pub fn shrink_to_fit(&mut self) { |
416 | self.events.shrink_to_fit(); |
417 | } |
418 | |
419 | /// Returns an iterator over the `epoll::Event`s in this `epoll::EventVec`. |
420 | #[inline ] |
421 | pub fn iter(&self) -> Iter<'_> { |
422 | Iter { |
423 | iter: self.events.iter().copied(), |
424 | } |
425 | } |
426 | |
427 | /// Returns the number of `epoll::Event`s logically contained in this |
428 | /// `epoll::EventVec`. |
429 | #[inline ] |
430 | pub fn len(&mut self) -> usize { |
431 | self.events.len() |
432 | } |
433 | |
434 | /// Tests whether this `epoll::EventVec` is logically empty. |
435 | #[inline ] |
436 | pub fn is_empty(&mut self) -> bool { |
437 | self.events.is_empty() |
438 | } |
439 | } |
440 | |
441 | #[cfg (feature = "alloc" )] |
442 | impl<'a> IntoIterator for &'a EventVec { |
443 | type IntoIter = Iter<'a>; |
444 | type Item = epoll::Event; |
445 | |
446 | #[inline ] |
447 | fn into_iter(self) -> Self::IntoIter { |
448 | self.iter() |
449 | } |
450 | } |
451 | |
452 | #[cfg (test)] |
453 | mod tests { |
454 | use super::*; |
455 | |
456 | #[test ] |
457 | fn test_epoll_layouts() { |
458 | check_renamed_type!(Event, epoll_event); |
459 | check_renamed_struct_renamed_field!(Event, epoll_event, flags, events); |
460 | #[cfg (libc)] |
461 | check_renamed_struct_renamed_field!(Event, epoll_event, data, u64); |
462 | #[cfg (not(libc))] |
463 | check_renamed_struct_renamed_field!(Event, epoll_event, data, data); |
464 | } |
465 | } |
466 | |