iceoryx2_bb_linux/
epoll.rs

1// Copyright (c) 2025 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13//! [`Epoll`] is a safe abstraction over the event file descriptor in linux. It allows users to
14//! attach [`FileDescriptor`]s with a set of [`EventType`]s and [`InputFlag`]s. Additionally,
15//! [`Epoll`] can also handle [`FetchableSignal`]s via a wakeup and informing the user what
16//! [`FetchableSignal`] was raised.
17//!
18//! # Example
19//!
20//! ## Handle Events
21//!
22//! ```
23//! # extern crate iceoryx2_loggers;
24//!
25//! use iceoryx2_bb_linux::epoll::*;
26//! use iceoryx2_bb_posix::socket_pair::StreamingSocket;
27//! use iceoryx2_bb_posix::file_descriptor::FileDescriptorBased;
28//!
29//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
30//!
31//! let epoll = EpollBuilder::new().create()?;
32//! let (socket_1, socket_2) = StreamingSocket::create_pair()?;
33//!
34//! let epoll_guard = epoll
35//!     .add(socket_1.file_descriptor())
36//!     .event_type(EventType::ReadyToRead)
37//!     .attach()?;
38//!
39//! socket_2.try_send(b"hello world")?;
40//!
41//! let number_of_triggers = epoll.blocking_wait(|event| {
42//!     if let EpollEvent::FileDescriptor(fd_event) = event {
43//!         if fd_event.originates_from(socket_1.file_descriptor()) {
44//!             let mut raw_data = [0u8; 20];
45//!             socket_1.try_receive(&mut raw_data);
46//!         }
47//!     }
48//! })?;
49//!
50//! # Ok(())
51//! # }
52//! ```
53
54extern crate alloc;
55
56use alloc::format;
57
58use core::mem::MaybeUninit;
59use core::time::Duration;
60use iceoryx2_bb_concurrency::atomic::Ordering;
61
62use iceoryx2_bb_concurrency::atomic::AtomicUsize;
63use iceoryx2_bb_container::semantic_string::SemanticString;
64use iceoryx2_bb_posix::{
65    file::{AccessMode, FileBuilder, FileOpenError, FileReadError},
66    file_descriptor::{FileDescriptor, FileDescriptorBased},
67    signal::FetchableSignal,
68    signal_set::FetchableSignalSet,
69};
70use iceoryx2_bb_system_types::file_path::FilePath;
71use iceoryx2_log::{fail, warn};
72use iceoryx2_pal_os_api::linux;
73use iceoryx2_pal_posix::posix::{self};
74
75use crate::signalfd::{SignalFd, SignalFdBuilder, SignalFdReadError, SignalInfo};
76
77const MAX_USER_WATCHES_FILE: &str = "/proc/sys/fs/epoll/max_user_watches";
78
79/// Errors that can occur when [`EpollBuilder::create()`] creates a new [`Epoll`].
80#[derive(Debug, Clone, Copy, Eq, PartialEq)]
81pub enum EpollCreateError {
82    /// The process file handle limit has been reached.
83    PerProcessFileHandleLimitReached,
84    /// The system wide file handle limit has been reached.
85    SystemWideFileHandleLimitReached,
86    /// The system has not enough memory to create the [`Epoll`]
87    InsufficientMemory,
88    /// The syscall [`linux::epoll_create()`] returned a broken [`FileDescriptor`].
89    SysCallReturnedInvalidFileDescriptor,
90    /// [`EpollBuilder`] was configured to handle some [`FetchableSignal`]s but the underlying
91    /// [`SignalFd`] could not be created.
92    UnableToEnableSignalHandling,
93    /// An error occurred that was not described in the linux man-page.
94    UnknownError(i32),
95}
96
97impl core::fmt::Display for EpollCreateError {
98    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
99        write!(f, "EpollCreateError::{self:?}")
100    }
101}
102
103impl core::error::Error for EpollCreateError {}
104
105/// Can be emitted by [`Epoll::add()`] when a new [`FileDescriptor`] shall be attached.
106#[derive(Debug, Clone, Copy, Eq, PartialEq)]
107pub enum EpollAttachmentError {
108    /// The [`FileDescriptor`] is already attached.
109    AlreadyAttached,
110    /// The system has not enough memory to attach the [`FileDescriptor`].
111    InsufficientMemory,
112    /// The maximum supported amount of [`FileDescriptor`]s are already attached to [`Epoll`].
113    ExceedsMaxSupportedAttachments,
114    /// A [`FileDescriptor`] that does not support event multiplexing was given. For instance a
115    /// [`FileDescriptor`] of a [`File`](iceoryx2_bb_posix::file::File) or a
116    /// [`UnnamedSemaphore`](iceoryx2_bb_posix::semaphore::UnnamedSemaphore).
117    ProvidedFileDescriptorDoesNotSupportEventMultiplexing,
118    /// An error occurred that was not described in the linux man-page.
119    UnknownError(i32),
120}
121
122impl core::fmt::Display for EpollAttachmentError {
123    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124        write!(f, "EpollAttachmentError::{self:?}")
125    }
126}
127
128impl core::error::Error for EpollAttachmentError {}
129
130/// Can be emitted by [`Epoll::capacity()`] when the epoll capacity is read from the proc
131/// file system.
132#[derive(Debug, Clone, Copy, Eq, PartialEq)]
133pub enum EpollGetCapacityError {
134    /// The proc file containing the capacity does not exist.
135    ProcFileDoesNotExist,
136    /// The content of the proc file could not be read.
137    ProcFileReadFailure,
138    /// The process does not have the permission to open the proc file for reading.
139    InsufficientPermissions,
140    /// Insufficient memory to read from the proc file.
141    InsufficientMemory,
142    /// The process file handle limit has been reached.
143    PerProcessFileHandleLimitReached,
144    /// The system wide file handle limit has been reached.
145    SystemWideFileHandleLimitReached,
146    /// [`FetchableSignal::Interrupt`] was received (SIGINT).
147    Interrupt,
148    /// The proc file does not contain a number but something else.
149    InvalidProcFileContent,
150    /// An undocumented error occurred.
151    UnknownError,
152}
153
154impl core::fmt::Display for EpollGetCapacityError {
155    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
156        write!(f, "EpollGetCapacityError::{self:?}")
157    }
158}
159
160impl core::error::Error for EpollGetCapacityError {}
161
162/// Errors that can be returned by [`Epoll::try_wait()`], [`Epoll::timed_wait()`] or
163/// [`Epoll::blocking_wait()`].
164#[derive(Debug, Clone, Copy, Eq, PartialEq)]
165pub enum EpollWaitError {
166    /// [`FetchableSignal::Interrupt`] was received (SIGINT).
167    Interrupt,
168    /// An error occurred that was not described in the linux man-page.
169    UnknownError(i32),
170}
171
172impl core::fmt::Display for EpollWaitError {
173    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
174        write!(f, "EpollWaitError::{self:?}")
175    }
176}
177
178impl core::error::Error for EpollWaitError {}
179
180/// Defines the type of event to which [`Epoll`] shall listen with the attached
181/// [`FileDescriptor`]
182#[derive(Debug, PartialEq, Eq, Clone, Copy)]
183#[repr(u32)]
184pub enum EventType {
185    /// Detect when the [`FileDescriptor`] has data to read.
186    ReadyToRead = linux::EPOLL_EVENTS_EPOLLIN as _,
187    /// Detect when the [`FileDescriptor`] is able to write data.
188    ReadyToWrite = linux::EPOLL_EVENTS_EPOLLOUT as _,
189    /// Detect when the [`FileDescriptor`]s counterpart closed the connection.
190    ConnectionClosed = linux::EPOLL_EVENTS_EPOLLRDHUP as _,
191    /// Detect an exceptional condition on the [`FileDescriptor`].
192    ExceptionalCondition = linux::EPOLL_EVENTS_EPOLLPRI as _,
193    /// Detect an error condition on the [`FileDescriptor`].
194    ErrorCondition = linux::EPOLL_EVENTS_EPOLLERR as _,
195    /// Detect when the [`FileDescriptor`]s counterpart closed the connection.
196    Hangup = linux::EPOLL_EVENTS_EPOLLHUP as _,
197}
198
199/// The input flags for the [`FileDescriptor`] attachments.
200#[derive(Debug, PartialEq, Eq, Clone, Copy)]
201#[repr(u32)]
202pub enum InputFlag {
203    /// Use edge triggered notifications instead of level triggered notifications.
204    /// See `man epoll` for more details.
205    EdgeTriggeredNotification = linux::EPOLL_EVENTS_EPOLLET as _,
206    /// Enable one-shot notification for the attached [`FileDescriptor`]. If the state changed
207    /// the user is notified once in the wait calls. The user must detach and reattach the
208    /// [`FileDescriptor`] to rearm it again.
209    OneShotNotification = linux::EPOLL_EVENTS_EPOLLONESHOT as _,
210    /// Ensures that the system does not enter "suspend" or "hibernate" while this event is
211    /// pending or being processed.
212    BlockSuspension = linux::EPOLL_EVENTS_EPOLLWAKEUP as _,
213    /// Sets an exclusive wakeup mode for the [`FileDescriptor`]. Useful when multiple epoll
214    /// file descriptors are attached to the same target file.
215    ExclusiveWakeup = linux::EPOLL_EVENTS_EPOLLEXCLUSIVE as _,
216}
217
218/// Returned by [`EpollAttachmentBuilder::attach()`] and represents an [`Epoll`] attachment. As
219/// soon as the [`EpollGuard`] goes out of scope the attachment is detached.
220pub struct EpollGuard<'epoll, 'file_descriptor> {
221    epoll: &'epoll Epoll,
222    fd: &'file_descriptor FileDescriptor,
223}
224
225impl<'epoll, 'file_descriptor> EpollGuard<'epoll, 'file_descriptor> {
226    /// Returns a reference of the attached [`FileDescriptor`]
227    pub fn file_descriptor(&self) -> &'file_descriptor FileDescriptor {
228        self.fd
229    }
230}
231
232impl Drop for EpollGuard<'_, '_> {
233    fn drop(&mut self) {
234        self.epoll.remove(unsafe { self.fd.native_handle() })
235    }
236}
237
238/// The builder to create a new [`Epoll`].
239#[derive(Debug)]
240pub struct EpollBuilder {
241    has_close_on_exec_flag: bool,
242    signal_set: FetchableSignalSet,
243    has_enabled_signal_handling: bool,
244}
245
246impl Default for EpollBuilder {
247    fn default() -> Self {
248        Self::new()
249    }
250}
251
252impl EpollBuilder {
253    /// Creates a new builder instance.
254    pub fn new() -> Self {
255        Self {
256            has_close_on_exec_flag: false,
257            signal_set: FetchableSignalSet::new_empty(),
258            has_enabled_signal_handling: false,
259        }
260    }
261
262    /// Defines if the underlying [`FileDescriptor`] shall be closed when the
263    /// [`Process`](iceoryx2_bb_posix::process::Process) is forked.
264    pub fn set_close_on_exec(mut self, value: bool) -> Self {
265        self.has_close_on_exec_flag = value;
266        self
267    }
268
269    /// Defines all [`FetchableSignal`]s the [`Epoll`] shall handle. When one of the defined
270    /// [`FetchableSignal`]s is raised [`Epoll::try_wait()`], [`Epoll::timed_wait()`] or
271    /// [`Epoll::blocking_wait()`] will wake up and provide the signal details in
272    /// [`EventType`] as [`SignalInfo`].
273    pub fn handle_signal(mut self, signal: FetchableSignal) -> Self {
274        self.signal_set.add(signal);
275        self.has_enabled_signal_handling = true;
276        self
277    }
278
279    /// Creates a new [`Epoll`].
280    pub fn create(self) -> Result<Epoll, EpollCreateError> {
281        let msg = "Unable to create epoll file descriptor";
282        let mut flags = 0;
283        if self.has_close_on_exec_flag {
284            flags |= linux::EPOLL_CLOEXEC;
285        }
286
287        let epoll_fd = unsafe { linux::epoll_create1(flags as _) };
288        if epoll_fd == -1 {
289            match posix::Errno::get() {
290                posix::Errno::EMFILE => {
291                    fail!(from self, with EpollCreateError::PerProcessFileHandleLimitReached,
292                        "{msg} since it would exceed the process limit for file descriptors.");
293                }
294                posix::Errno::ENFILE => {
295                    fail!(from self, with EpollCreateError::SystemWideFileHandleLimitReached,
296                        "{msg} since it would exceed the system limit for file descriptors.");
297                }
298                posix::Errno::ENOMEM => {
299                    fail!(from self, with EpollCreateError::InsufficientMemory,
300                        "{msg} due to insufficient memory.");
301                }
302                e => {
303                    fail!(from self, with EpollCreateError::UnknownError(e as i32),
304                        "{msg} since an unknown error occurred ({e:?}).");
305                }
306            }
307        }
308
309        let epoll_fd = match FileDescriptor::new(epoll_fd) {
310            Some(fd) => fd,
311            None => {
312                fail!(from self, with EpollCreateError::SysCallReturnedInvalidFileDescriptor,
313                        "{msg} since the epoll_create1() syscall returned an invalid file descriptor.");
314            }
315        };
316
317        if !self.has_enabled_signal_handling {
318            return Ok(Epoll {
319                len: AtomicUsize::new(0),
320                epoll_fd,
321                signal_fd: None,
322            });
323        }
324
325        let origin = format!("{self:?}");
326        let signal_fd = match SignalFdBuilder::new(self.signal_set)
327            .set_close_on_exec(self.has_close_on_exec_flag)
328            .create_non_blocking()
329        {
330            Ok(signal_fd) => signal_fd,
331            Err(e) => {
332                fail!(from origin, with EpollCreateError::UnableToEnableSignalHandling,
333                        "{msg} since the signal fd, required for signal handling, could not be created ({e:?}).");
334            }
335        };
336
337        let mut epoll_event: linux::epoll_event = unsafe { core::mem::zeroed() };
338        epoll_event.events = EventType::ReadyToRead as _;
339        unsafe {
340            core::ptr::copy_nonoverlapping(
341                (signal_fd.file_descriptor() as *const _) as *const u8,
342                linux::epoll_addr_of_event_data_mut(&mut epoll_event),
343                core::mem::size_of::<FileDescriptor>(),
344            )
345        };
346
347        if unsafe {
348            linux::epoll_ctl(
349                epoll_fd.native_handle(),
350                linux::EPOLL_CTL_ADD as _,
351                signal_fd.file_descriptor().native_handle(),
352                &mut epoll_event,
353            )
354        } == -1
355        {
356            match posix::Errno::get() {
357                posix::Errno::ENOMEM => {
358                    fail!(from origin, with EpollCreateError::InsufficientMemory,
359                        "{msg} since there is not enough memory available to attach the signalfd for signal handling.");
360                }
361                e => {
362                    fail!(from origin, with EpollCreateError::UnknownError(e as i32),
363                        "{msg} due to an unknown error while attaching the signalfd for signal handling.");
364                }
365            }
366        }
367
368        Ok(Epoll {
369            epoll_fd,
370            signal_fd: Some(signal_fd),
371            len: AtomicUsize::new(0),
372        })
373    }
374}
375
376/// Represents an event that activated the [`Epoll`].
377pub enum EpollEvent<'a> {
378    /// A [`FileDescriptor`] was activated.
379    FileDescriptor(FileDescriptorEvent<'a>),
380    /// A [`FetchableSignal`] was raised.
381    Signal(SignalInfo),
382}
383
384/// Describes an event on a [`FileDescriptor`].
385pub struct FileDescriptorEvent<'a> {
386    data: &'a linux::epoll_event,
387}
388
389impl FileDescriptorEvent<'_> {
390    /// Returns `true` if the [`FileDescriptorEvent`] originated from the provided
391    /// [`FileDescriptor`], otherwise `false`.
392    pub fn originates_from(&self, file_descriptor: &FileDescriptor) -> bool {
393        unsafe { file_descriptor.native_handle() == self.native_fd_handle() }
394    }
395
396    /// Returns the native handle of the corresponding [`FileDescriptor`]
397    ///
398    /// # Safety
399    ///
400    /// * the user must not modify or close the provided native handle
401    pub unsafe fn native_fd_handle(&self) -> i32 {
402        let mut native_handle: i32 = 0;
403        unsafe {
404            core::ptr::copy_nonoverlapping(
405                linux::epoll_addr_of_event_data(self.data),
406                (&mut native_handle as *mut i32).cast(),
407                core::mem::size_of::<i32>(),
408            )
409        }
410        native_handle
411    }
412
413    /// Returns `true` if the [`FileDescriptorEvent`] was caused by the provided [`EventType`],
414    /// otherwise `false`.
415    pub fn has_event(&self, event_type: EventType) -> bool {
416        self.data.events & event_type as u32 != 0
417    }
418}
419
420/// Abstraction of the event multiplexer epoll.
421#[derive(Debug)]
422pub struct Epoll {
423    epoll_fd: FileDescriptor,
424    signal_fd: Option<SignalFd>,
425    len: AtomicUsize,
426}
427
428impl Epoll {
429    fn remove(&self, fd_value: i32) {
430        if unsafe {
431            linux::epoll_ctl(
432                self.epoll_fd.native_handle(),
433                linux::EPOLL_CTL_DEL as _,
434                fd_value,
435                core::ptr::null_mut(),
436            )
437        } == -1
438        {
439            warn!(from self,
440                "This should never happen! Failed to detach {fd_value} from epoll due to ({:?}). This might cause unexpected behavior.",
441                posix::Errno::get());
442        } else {
443            self.len.fetch_sub(1, Ordering::Relaxed);
444        }
445    }
446
447    /// Returns the number of wait events that can be handle at most with one
448    /// [`Epoll::try_wait()`], [`Epoll::timed_wait()`] or [`Epoll::blocking_wait()`] call.
449    pub const fn max_wait_events() -> usize {
450        512
451    }
452
453    /// Returns the maximum supported [`Epoll`] capacity for the current system.
454    pub fn capacity() -> Result<usize, EpollGetCapacityError> {
455        let origin = "Epoll::capacity()";
456        let msg = "Unable to acquire the capacity of epoll";
457        let file_path = unsafe { FilePath::new_unchecked(MAX_USER_WATCHES_FILE.as_bytes()) };
458        let proc_stat_file = match FileBuilder::new(&file_path)
459            .has_ownership(false)
460            .open_existing(AccessMode::Read)
461        {
462            Ok(file) => file,
463            Err(FileOpenError::FileDoesNotExist) => {
464                fail!(from origin, with EpollGetCapacityError::ProcFileDoesNotExist,
465                    "{msg} since the file {MAX_USER_WATCHES_FILE} does not exist.");
466            }
467            Err(FileOpenError::InsufficientPermissions) => {
468                fail!(from origin, with EpollGetCapacityError::InsufficientPermissions,
469                    "{msg} since the file {MAX_USER_WATCHES_FILE} could not be opened due to insufficient permissions.");
470            }
471            Err(FileOpenError::Interrupt) => {
472                fail!(from origin, with EpollGetCapacityError::Interrupt,
473                    "{msg} since an interrupt signal was raised while opening the file {MAX_USER_WATCHES_FILE}.");
474            }
475            Err(FileOpenError::InsufficientMemory) => {
476                fail!(from origin, with EpollGetCapacityError::InsufficientMemory,
477                    "{msg} since the file {MAX_USER_WATCHES_FILE} could not be opened due to insufficient memory.");
478            }
479            Err(FileOpenError::PerProcessFileHandleLimitReached) => {
480                fail!(from origin, with EpollGetCapacityError::PerProcessFileHandleLimitReached,
481                    "{msg} since the process file handle limit was reached while opening {MAX_USER_WATCHES_FILE}.");
482            }
483            Err(FileOpenError::SystemWideFileHandleLimitReached) => {
484                fail!(from origin, with EpollGetCapacityError::SystemWideFileHandleLimitReached,
485                    "{msg} since the system wide file handle limit was reached while opening {MAX_USER_WATCHES_FILE}.");
486            }
487            Err(e) => {
488                fail!(from origin, with EpollGetCapacityError::UnknownError,
489                    "{msg} due to an unknown error while opening {MAX_USER_WATCHES_FILE} ({e:?}).");
490            }
491        };
492
493        let mut buffer = [0u8; 32];
494        let bytes_read = match proc_stat_file.read(&mut buffer) {
495            Ok(v) => v,
496            Err(FileReadError::Interrupt) => {
497                fail!(from origin, with EpollGetCapacityError::Interrupt,
498                    "{msg} since an interrupt signal was raised while reading the file {MAX_USER_WATCHES_FILE}.");
499            }
500            Err(e) => {
501                fail!(from origin, with EpollGetCapacityError::ProcFileReadFailure,
502                    "{msg} since the content of the file {MAX_USER_WATCHES_FILE} could not be read ({e:?}).");
503            }
504        };
505
506        let file_content = match core::str::from_utf8(&buffer[0..bytes_read as usize - 1]) {
507            Ok(v) => v,
508            Err(e) => {
509                fail!(from origin, with EpollGetCapacityError::InvalidProcFileContent,
510                "{msg} since the file {MAX_USER_WATCHES_FILE} contains invalid content. Expected an UTF-8 string. ({e:?})");
511            }
512        };
513
514        match file_content.parse::<usize>() {
515            Ok(v) => Ok(v),
516            Err(e) => {
517                fail!(from origin, with EpollGetCapacityError::InvalidProcFileContent,
518                    "{msg} since the file {MAX_USER_WATCHES_FILE} contains invalid content. Expected a number. ({e:?})");
519            }
520        }
521    }
522
523    /// Returns `true` when [`Epoll`] has no attached [`FileDescriptor`]s, otherwise `false`.
524    pub fn is_empty(&self) -> bool {
525        self.len() == 0
526    }
527
528    /// Returns the number of attached [`FileDescriptor`]s.
529    pub fn len(&self) -> usize {
530        self.len.load(Ordering::Relaxed)
531    }
532
533    /// Returns an [`EpollAttachmentBuilder`] to attach a new [`FileDescriptor`] to [`Epoll`].
534    pub fn add<'epoll, 'fd>(
535        &'epoll self,
536        fd: &'fd FileDescriptor,
537    ) -> EpollAttachmentBuilder<'epoll, 'fd> {
538        EpollAttachmentBuilder {
539            epoll: self,
540            fd,
541            events_flag: 0,
542        }
543    }
544
545    /// Non-blocking call, that returns the number of activated attachments and calls the provided
546    /// callback for every activated attachment and with [`EpollEvent`] as callback argument
547    /// that contains the information about the activated attachment.
548    pub fn try_wait<F: FnMut(EpollEvent)>(&self, event_call: F) -> Result<usize, EpollWaitError> {
549        self.wait_impl(0, event_call)
550    }
551
552    /// Blocking call, that returns the number of activated attachments and calls the provided
553    /// callback for every activated attachment and with [`EpollEvent`] as callback argument
554    /// that contains the information about the activated attachment.
555    /// If the timeout has passed and no activation has happened it will return 0.
556    pub fn timed_wait<F: FnMut(EpollEvent)>(
557        &self,
558        event_call: F,
559        timeout: Duration,
560    ) -> Result<usize, EpollWaitError> {
561        // the smallest time period epoll can wait is 1ms, to introduce some waiting for
562        // smaller time periods we always round the timeout up to the next millisecond
563        let timeout_in_ms = timeout.as_nanos().div_ceil(1_000_000) as i32;
564        self.wait_impl(timeout_in_ms, event_call)
565    }
566
567    /// Blocking call, that returns the number of activated attachments and calls the provided
568    /// callback for every activated attachment and with [`EpollEvent`] as callback argument
569    /// that contains the information about the activated attachment.
570    pub fn blocking_wait<F: FnMut(EpollEvent)>(
571        &self,
572        event_call: F,
573    ) -> Result<usize, EpollWaitError> {
574        self.wait_impl(-1, event_call)
575    }
576
577    fn wait_impl<F: FnMut(EpollEvent)>(
578        &self,
579        timeout: posix::int,
580        mut event_call: F,
581    ) -> Result<usize, EpollWaitError> {
582        let msg = "Unable to wait on epoll";
583
584        let mut events: [MaybeUninit<linux::epoll_event>; Self::max_wait_events()] =
585            [MaybeUninit::uninit(); Self::max_wait_events()];
586
587        let number_of_fds = unsafe {
588            linux::epoll_wait(
589                self.epoll_fd.native_handle(),
590                events.as_mut_ptr().cast(),
591                Self::max_wait_events() as _,
592                timeout,
593            )
594        };
595
596        if number_of_fds == -1 {
597            match posix::Errno::get() {
598                posix::Errno::EINTR => {
599                    fail!(from self, with EpollWaitError::Interrupt,
600                        "{msg} with a timeout of {timeout}ms since an interrupt signal was raised."
601                    );
602                }
603                e => {
604                    fail!(from self, with EpollWaitError::UnknownError(e as i32),
605                        "{msg} with a timeout of {timeout}ms due to an unknown failure ({e:?})."
606                    );
607                }
608            }
609        }
610
611        match self.signal_fd.as_ref() {
612            Some(signal_fd) => {
613                for i in 0..number_of_fds {
614                    let fd_event = FileDescriptorEvent {
615                        data: unsafe { events[i as usize].assume_init_ref() },
616                    };
617
618                    if fd_event.originates_from(signal_fd.file_descriptor()) {
619                        while let Some(signal) = match signal_fd.try_read() {
620                            Ok(v) => v,
621                            Err(SignalFdReadError::Interrupt) => {
622                                fail!(from self, with EpollWaitError::Interrupt,
623                                    "{msg} with a timeout of {timeout}ms since an interrupt signal was raised while acquiring the raised signals.");
624                            }
625                            Err(e) => {
626                                warn!("Epoll wait will continue but a failure occurred while reading the raised signal ({e:?}).");
627                                None
628                            }
629                        } {
630                            event_call(EpollEvent::Signal(signal));
631                        }
632                    } else {
633                        event_call(EpollEvent::FileDescriptor(fd_event));
634                    }
635                }
636            }
637            None => {
638                for i in 0..number_of_fds {
639                    event_call(EpollEvent::FileDescriptor(FileDescriptorEvent {
640                        data: unsafe { events[i as usize].assume_init_ref() },
641                    }));
642                }
643            }
644        }
645
646        Ok(number_of_fds as usize)
647    }
648}
649
650/// Builder created by [`Epoll::add()`] that configures the [`EventType`]s and the [`InputFlag`]s
651/// of the attachment.
652#[derive(Debug)]
653pub struct EpollAttachmentBuilder<'epoll, 'fd> {
654    epoll: &'epoll Epoll,
655    fd: &'fd FileDescriptor,
656    events_flag: u32,
657}
658
659impl<'epoll, 'fd> EpollAttachmentBuilder<'epoll, 'fd> {
660    /// The user can call this multiple times to define multiple [`EventType`]s for the attachment.
661    /// It defines the [`EventType`] that shall cause a wakeup in [`Epoll`].
662    pub fn event_type(mut self, event_type: EventType) -> Self {
663        self.events_flag |= event_type as u32;
664        self
665    }
666
667    /// The user can call this multiple times to attach multiple [`InputFlag`]s for the attachment.
668    pub fn flags(mut self, input_flag: InputFlag) -> Self {
669        self.events_flag |= input_flag as u32;
670        self
671    }
672
673    /// Attaches the [`FileDescriptor`] to [`Epoll`] and returns an [`EpollGuard`]. As soon as the
674    /// [`EpollGuard`] goes out-of-scope the attachment is released.
675    pub fn attach(self) -> Result<EpollGuard<'epoll, 'fd>, EpollAttachmentError> {
676        let msg = "Unable to attach file descriptor to epoll";
677        let mut epoll_event: linux::epoll_event = unsafe { core::mem::zeroed() };
678
679        epoll_event.events = self.events_flag;
680        unsafe {
681            core::ptr::copy_nonoverlapping(
682                (self.fd as *const _) as *const u8,
683                linux::epoll_addr_of_event_data_mut(&mut epoll_event),
684                core::mem::size_of::<FileDescriptor>(),
685            )
686        }
687
688        if unsafe {
689            linux::epoll_ctl(
690                self.epoll.epoll_fd.native_handle(),
691                linux::EPOLL_CTL_ADD as _,
692                self.fd.native_handle(),
693                &mut epoll_event,
694            )
695        } == -1
696        {
697            match posix::Errno::get() {
698                posix::Errno::EEXIST => {
699                    fail!(from self, with EpollAttachmentError::AlreadyAttached,
700                        "{msg} since it is already attached.");
701                }
702                posix::Errno::ENOMEM => {
703                    fail!(from self, with EpollAttachmentError::InsufficientMemory,
704                        "{msg} due to insufficient memory.");
705                }
706                posix::Errno::ENOSPC => {
707                    fail!(from self, with EpollAttachmentError::ExceedsMaxSupportedAttachments,
708                        "{msg} since it would exceed the system limit of the number of attachments.");
709                }
710                posix::Errno::EPERM => {
711                    fail!(from self, with EpollAttachmentError::ProvidedFileDescriptorDoesNotSupportEventMultiplexing,
712                        "{msg} since the provided file descriptor does not support event multiplexing.");
713                }
714                e => {
715                    fail!(from self, with EpollAttachmentError::UnknownError(e as i32),
716                        "{msg} due to an unknown error ({e:?}).");
717                }
718            }
719        }
720
721        self.epoll.len.fetch_add(1, Ordering::Relaxed);
722
723        Ok(EpollGuard {
724            epoll: self.epoll,
725            fd: self.fd,
726        })
727    }
728}