1extern 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#[derive(Debug, Clone, Copy, Eq, PartialEq)]
81pub enum EpollCreateError {
82 PerProcessFileHandleLimitReached,
84 SystemWideFileHandleLimitReached,
86 InsufficientMemory,
88 SysCallReturnedInvalidFileDescriptor,
90 UnableToEnableSignalHandling,
93 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#[derive(Debug, Clone, Copy, Eq, PartialEq)]
107pub enum EpollAttachmentError {
108 AlreadyAttached,
110 InsufficientMemory,
112 ExceedsMaxSupportedAttachments,
114 ProvidedFileDescriptorDoesNotSupportEventMultiplexing,
118 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#[derive(Debug, Clone, Copy, Eq, PartialEq)]
133pub enum EpollGetCapacityError {
134 ProcFileDoesNotExist,
136 ProcFileReadFailure,
138 InsufficientPermissions,
140 InsufficientMemory,
142 PerProcessFileHandleLimitReached,
144 SystemWideFileHandleLimitReached,
146 Interrupt,
148 InvalidProcFileContent,
150 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#[derive(Debug, Clone, Copy, Eq, PartialEq)]
165pub enum EpollWaitError {
166 Interrupt,
168 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#[derive(Debug, PartialEq, Eq, Clone, Copy)]
183#[repr(u32)]
184pub enum EventType {
185 ReadyToRead = linux::EPOLL_EVENTS_EPOLLIN as _,
187 ReadyToWrite = linux::EPOLL_EVENTS_EPOLLOUT as _,
189 ConnectionClosed = linux::EPOLL_EVENTS_EPOLLRDHUP as _,
191 ExceptionalCondition = linux::EPOLL_EVENTS_EPOLLPRI as _,
193 ErrorCondition = linux::EPOLL_EVENTS_EPOLLERR as _,
195 Hangup = linux::EPOLL_EVENTS_EPOLLHUP as _,
197}
198
199#[derive(Debug, PartialEq, Eq, Clone, Copy)]
201#[repr(u32)]
202pub enum InputFlag {
203 EdgeTriggeredNotification = linux::EPOLL_EVENTS_EPOLLET as _,
206 OneShotNotification = linux::EPOLL_EVENTS_EPOLLONESHOT as _,
210 BlockSuspension = linux::EPOLL_EVENTS_EPOLLWAKEUP as _,
213 ExclusiveWakeup = linux::EPOLL_EVENTS_EPOLLEXCLUSIVE as _,
216}
217
218pub 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 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#[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 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 pub fn set_close_on_exec(mut self, value: bool) -> Self {
265 self.has_close_on_exec_flag = value;
266 self
267 }
268
269 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 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
376pub enum EpollEvent<'a> {
378 FileDescriptor(FileDescriptorEvent<'a>),
380 Signal(SignalInfo),
382}
383
384pub struct FileDescriptorEvent<'a> {
386 data: &'a linux::epoll_event,
387}
388
389impl FileDescriptorEvent<'_> {
390 pub fn originates_from(&self, file_descriptor: &FileDescriptor) -> bool {
393 unsafe { file_descriptor.native_handle() == self.native_fd_handle() }
394 }
395
396 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 pub fn has_event(&self, event_type: EventType) -> bool {
416 self.data.events & event_type as u32 != 0
417 }
418}
419
420#[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 pub const fn max_wait_events() -> usize {
450 512
451 }
452
453 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 pub fn is_empty(&self) -> bool {
525 self.len() == 0
526 }
527
528 pub fn len(&self) -> usize {
530 self.len.load(Ordering::Relaxed)
531 }
532
533 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 pub fn try_wait<F: FnMut(EpollEvent)>(&self, event_call: F) -> Result<usize, EpollWaitError> {
549 self.wait_impl(0, event_call)
550 }
551
552 pub fn timed_wait<F: FnMut(EpollEvent)>(
557 &self,
558 event_call: F,
559 timeout: Duration,
560 ) -> Result<usize, EpollWaitError> {
561 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 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#[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 pub fn event_type(mut self, event_type: EventType) -> Self {
663 self.events_flag |= event_type as u32;
664 self
665 }
666
667 pub fn flags(mut self, input_flag: InputFlag) -> Self {
669 self.events_flag |= input_flag as u32;
670 self
671 }
672
673 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}