iceoryx2_bb_posix/
file_descriptor_set.rs

1// Copyright (c) 2023 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//! Abstracts a POSIX file descriptor set based of FD_* and select.
14//! Can be used to wait on multiple objects which implement the [`SynchronousMultiplexing`]
15//! trait.
16//!
17//! # Example
18//!
19//! ```ignore
20//! # extern crate iceoryx2_loggers;
21//!
22//! use iceoryx2_bb_posix::file_descriptor_set::*;
23//! use iceoryx2_bb_posix::unix_datagram_socket::*;
24//! use core::time::Duration;
25//! use iceoryx2_bb_system_types::file_path::FilePath;
26//! use iceoryx2_bb_container::semantic_string::SemanticString;
27//!
28//! let socket_name = FilePath::new(b"some_socket").unwrap();
29//!
30//! let sut_receiver = UnixDatagramReceiverBuilder::new(&socket_name)
31//!     .creation_mode(CreationMode::PurgeAndCreate)
32//!     .create()
33//!     .unwrap();
34//!
35//! let sut_sender = UnixDatagramSenderBuilder::new(&socket_name)
36//!     .create()
37//!     .unwrap();
38//!
39//! let fd_set = FileDescriptorSet::new();
40//! fd_set.add(&sut_receiver);
41//! let send_data: Vec<u8> = vec![1u8, 3u8, 3u8, 7u8, 13u8, 37u8];
42//! sut_sender.try_send(send_data.as_slice()).unwrap();
43//!
44//! // in some other process
45//! let result = fd_set.timed_wait(Duration::from_secs(1), FileEvent::Read,
46//!     |fd| println!("Fd was triggered {}", unsafe { fd.native_handle() })).unwrap();
47//! ```
48
49use core::{fmt::Debug, time::Duration};
50
51use alloc::vec;
52use alloc::vec::Vec;
53
54use crate::{
55    clock::AsTimeval,
56    file_descriptor::{FileDescriptor, FileDescriptorBased},
57};
58use iceoryx2_bb_concurrency::cell::UnsafeCell;
59use iceoryx2_log::fail;
60use iceoryx2_pal_posix::posix::{errno::Errno, MemZeroedStruct};
61use iceoryx2_pal_posix::*;
62
63/// A trait which is implement by all objects which can be added to the [`FileDescriptorSet`].
64pub trait SynchronousMultiplexing: FileDescriptorBased {}
65
66#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
67pub enum FileDescriptorSetWaitError {
68    Interrupt,
69    TooManyAttachedFileDescriptors,
70    InsufficientPermissions,
71    UnknownError(i32),
72}
73
74#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
75pub enum FileDescriptorSetAddError {
76    AlreadyAttached,
77    CapacityExceeded,
78}
79
80/// Defines the event type one wants to wait on in
81/// [`FileDescriptorSet::timed_wait()`]
82#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
83pub enum FileEvent {
84    Read,
85    Write,
86    Exceptional,
87    ReadWrite,
88    ReadExceptional,
89    WriteExceptional,
90    ReadWriteExceptional,
91}
92
93pub struct FileDescriptorSetGuard<'set, 'fd> {
94    set: &'set FileDescriptorSet,
95    fd: &'fd FileDescriptor,
96}
97
98impl<'fd> FileDescriptorSetGuard<'_, 'fd> {
99    pub fn file_descriptor(&self) -> &'fd FileDescriptor {
100        self.fd
101    }
102}
103
104impl Drop for FileDescriptorSetGuard<'_, '_> {
105    fn drop(&mut self) {
106        self.set.remove(unsafe { self.fd.native_handle() })
107    }
108}
109
110/// The POSIX abstraction file descriptor set to wait on multiple objects which implement
111/// the [`SynchronousMultiplexing`] trait.
112pub struct FileDescriptorSet {
113    internals: UnsafeCell<Internals>,
114}
115
116struct Internals {
117    fd_set: posix::fd_set,
118    file_descriptors: Vec<i32>,
119    max_fd: i32,
120}
121
122impl Debug for FileDescriptorSet {
123    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124        write!(
125            f,
126            "FileDescriptorSet {{ file_descriptors: {:?}, max_fd: {} }}",
127            self.internals().file_descriptors,
128            self.internals().max_fd
129        )
130    }
131}
132
133impl Default for FileDescriptorSet {
134    fn default() -> Self {
135        let fd_set = FileDescriptorSet {
136            internals: UnsafeCell::new(Internals {
137                fd_set: posix::fd_set::new_zeroed(),
138                file_descriptors: vec![],
139                max_fd: 0,
140            }),
141        };
142
143        unsafe { posix::FD_ZERO(&mut fd_set.internals_mut().fd_set) };
144
145        fd_set
146    }
147}
148
149impl FileDescriptorSet {
150    fn internals(&self) -> &Internals {
151        unsafe { &*self.internals.get() }
152    }
153
154    #[allow(clippy::mut_from_ref)]
155    fn internals_mut(&self) -> &mut Internals {
156        unsafe { &mut *self.internals.get() }
157    }
158
159    pub fn new() -> FileDescriptorSet {
160        FileDescriptorSet::default()
161    }
162
163    /// Adds a file descriptor
164    pub fn add<'set, 'fd, F: SynchronousMultiplexing>(
165        &'set self,
166        fd: &'fd F,
167    ) -> Result<FileDescriptorSetGuard<'set, 'fd>, FileDescriptorSetAddError> {
168        self.add_impl(fd.file_descriptor())
169    }
170
171    fn add_impl<'set, 'fd>(
172        &'set self,
173        fd: &'fd FileDescriptor,
174    ) -> Result<FileDescriptorSetGuard<'set, 'fd>, FileDescriptorSetAddError> {
175        let msg = "Unable to add file descriptor";
176        if self.internals().file_descriptors.len() >= Self::capacity() {
177            fail!(from self, with FileDescriptorSetAddError::CapacityExceeded,
178                "{msg} {:?} since the amount of file descriptors {} exceeds the maximum supported amount of file descriptors for a set {}.",
179                fd.file_descriptor(), self.internals().file_descriptors.len(), Self::capacity());
180        }
181
182        if self.contains_impl(fd) {
183            fail!(from self, with FileDescriptorSetAddError::AlreadyAttached,
184                "{msg} {:?} since it is already attached.", fd);
185        }
186
187        unsafe {
188            posix::FD_SET(
189                fd.file_descriptor().native_handle(),
190                &mut self.internals_mut().fd_set,
191            )
192        };
193        self.internals_mut().max_fd = core::cmp::max(
194            self.internals().max_fd,
195            unsafe { fd.file_descriptor().native_handle() } + 1,
196        );
197        self.internals_mut()
198            .file_descriptors
199            .push(unsafe { fd.file_descriptor().native_handle() });
200
201        Ok(FileDescriptorSetGuard { set: self, fd })
202    }
203
204    fn remove(&self, value: i32) {
205        unsafe { posix::FD_CLR(value, &mut self.internals_mut().fd_set) };
206
207        if self.internals_mut().max_fd == value + 1 {
208            self.internals_mut().max_fd = 0;
209            for fd in &self.internals().file_descriptors {
210                self.internals_mut().max_fd = core::cmp::max(self.internals().max_fd, fd + 1);
211            }
212        }
213
214        self.internals_mut()
215            .file_descriptors
216            .retain(|&v| value != v);
217    }
218
219    /// Returns the maximum capacity of the [`FileDescriptorSet`]
220    pub const fn capacity() -> usize {
221        posix::FD_SETSIZE
222    }
223
224    /// Returns the number of attached [`FileDescriptor`]s
225    pub fn len(&self) -> usize {
226        self.internals().file_descriptors.len()
227    }
228
229    /// Returns true if the [`FileDescriptorSet`] is empty, otherwise false
230    pub fn is_empty(&self) -> bool {
231        self.internals().file_descriptors.is_empty()
232    }
233
234    /// Returns true if the object is attached to the [`FileDescriptorSet`], otherwise false.
235    pub fn contains<T: SynchronousMultiplexing>(&self, fd: &T) -> bool {
236        self.contains_impl(fd.file_descriptor())
237    }
238
239    fn contains_impl(&self, fd: &FileDescriptor) -> bool {
240        unsafe { posix::FD_ISSET(fd.native_handle(), &self.internals().fd_set) }
241    }
242
243    /// Blocks until the specified event has occurred. It
244    /// returns a list with all [`FileDescriptor`]s which were triggered.
245    pub fn blocking_wait<F: FnMut(&FileDescriptor)>(
246        &self,
247        event: FileEvent,
248        fd_callback: F,
249    ) -> Result<usize, FileDescriptorSetWaitError> {
250        self.wait(core::ptr::null_mut(), event, fd_callback)
251    }
252
253    /// Waits until either the timeout has passed or the specified event has occurred. It
254    /// returns a list with all [`FileDescriptor`]s which were triggered.
255    pub fn timed_wait<F: FnMut(&FileDescriptor)>(
256        &self,
257        timeout: Duration,
258        event: FileEvent,
259        fd_callback: F,
260    ) -> Result<usize, FileDescriptorSetWaitError> {
261        let mut raw_timeout = timeout.as_timeval();
262        self.wait(&mut raw_timeout, event, fd_callback)
263    }
264
265    fn wait<F: FnMut(&FileDescriptor)>(
266        &self,
267        timeout: *mut posix::timeval,
268        event: FileEvent,
269        mut fd_callback: F,
270    ) -> Result<usize, FileDescriptorSetWaitError> {
271        let mut fd_set: posix::fd_set = self.internals().fd_set;
272
273        let read_fd: *mut posix::fd_set = match event {
274            FileEvent::Read
275            | FileEvent::ReadWrite
276            | FileEvent::ReadExceptional
277            | FileEvent::ReadWriteExceptional => &mut fd_set,
278            _ => core::ptr::null_mut::<posix::fd_set>(),
279        };
280        let write_fd: *mut posix::fd_set = match event {
281            FileEvent::Write
282            | FileEvent::ReadWrite
283            | FileEvent::WriteExceptional
284            | FileEvent::ReadWriteExceptional => &mut fd_set,
285            _ => core::ptr::null_mut::<posix::fd_set>(),
286        };
287        let exceptional_fd: *mut posix::fd_set = match event {
288            FileEvent::Exceptional
289            | FileEvent::ReadExceptional
290            | FileEvent::WriteExceptional
291            | FileEvent::ReadWriteExceptional => &mut fd_set,
292            _ => core::ptr::null_mut::<posix::fd_set>(),
293        };
294
295        let msg = "Failure while waiting for file descriptor events";
296        let number_of_notifications = unsafe {
297            posix::select(
298                self.internals().max_fd,
299                read_fd,
300                write_fd,
301                exceptional_fd,
302                timeout,
303            )
304        };
305
306        if number_of_notifications == -1 {
307            handle_errno!(FileDescriptorSetWaitError, from self,
308                fatal Errno::EBADF => ("This should never happen! {} since at least one of the attached file descriptors is invalid.", msg),
309                Errno::EINTR => (Interrupt, "{} since an interrupt signal was received.", msg),
310                Errno::EINVAL => (TooManyAttachedFileDescriptors,
311                    "{} since the number of attached file descriptors exceed the system limit of ({}).",
312                    msg, Self::capacity()),
313                Errno::EPERM => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
314                v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
315            );
316        }
317
318        for raw_fd in &self.internals().file_descriptors {
319            if unsafe { posix::FD_ISSET(*raw_fd, &fd_set) } {
320                let fd = FileDescriptor::non_owning_new(*raw_fd).unwrap();
321                fd_callback(&fd);
322            }
323        }
324
325        Ok(number_of_notifications as _)
326    }
327}