| 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 | |