iceoryx2_bb_posix/
read_write_mutex.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//! A POSIX inter-process capable [`ReadWriteMutex`] where either multiple readers can acquire
14//! multiple read-locks or one writer can acquire a write-lock.
15//! It is built by the [`ReadWriteMutexBuilder`].
16//!
17//! # Example
18//!
19//! ```no_run
20//! use iceoryx2_bb_posix::read_write_mutex::*;
21//! use std::thread;
22//! use core::time::Duration;
23//! use iceoryx2_bb_posix::clock::ClockType;
24//!
25//! let rw_handle = ReadWriteMutexHandle::new();
26//! let rw_mutex = ReadWriteMutexBuilder::new()
27//!                         .is_interprocess_capable(true)
28//!                         .create(123, &rw_handle)
29//!                         .expect("failed to create rw mutex");
30//!
31//! thread::scope(|s| {
32//!     s.spawn(|| {
33//!         let guard = rw_mutex.read_blocking_lock()
34//!                             .expect("failed to read_lock");
35//!         println!("The mutex value is: {}", *guard);
36//!     });
37//!
38//!     s.spawn(|| {
39//!         let mut guard = rw_mutex.write_blocking_lock()
40//!                                 .expect("failed to write_lock");
41//!         println!("The old value is: {}", *guard);
42//!         *guard = 456;
43//!         println!("The new value is: {}", *guard);
44//!     });
45//! });
46//! ```
47pub use crate::ipc_capable::{Handle, IpcCapable};
48
49use crate::handle_errno;
50use crate::ipc_capable::internal::{Capability, HandleStorage, IpcConstructible};
51use iceoryx2_bb_elementary::{enum_gen, scope_guard::ScopeGuardBuilder};
52use iceoryx2_bb_log::{fail, fatal_panic, warn};
53use iceoryx2_pal_posix::posix::errno::Errno;
54use iceoryx2_pal_posix::posix::Struct;
55use iceoryx2_pal_posix::*;
56
57use core::{
58    cell::UnsafeCell,
59    fmt::Debug,
60    ops::{Deref, DerefMut},
61};
62
63#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
64pub enum ReadWriteMutexCreationError {
65    InsufficientMemory,
66    InsufficientResources,
67    InsufficientPermissions,
68    NoInterProcessSupport,
69    NoMutexKindSupport,
70    UnknownError(i32),
71}
72
73#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
74pub enum ReadWriteMutexReadLockError {
75    MaximumAmountOfReadLocksAcquired,
76    DeadlockConditionDetected,
77    UnknownError(i32),
78}
79
80#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
81pub enum ReadWriteMutexUnlockError {
82    OwnedByDifferentEntity,
83    UnknownError(i32),
84}
85
86#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
87pub enum ReadWriteMutexWriteLockError {
88    DeadlockConditionDetected,
89    UnknownError(i32),
90}
91
92#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
93pub enum ReadWriteMutexOpenIpcHandleError {
94    IsNotInterProcessCapable,
95    Uninitialized,
96}
97
98enum_gen! {
99    /// The ReadWriteMutexError enum is a generalization when one doesn't require the fine-grained error
100    /// handling enums. One can forward ReadWriteMutexError as more generic return value when a method
101    /// returns a ReadWriteMutex***Error.
102    /// On a higher level it is again convertable to [`crate::Error`].
103    ReadWriteMutexError
104  generalization:
105    FailedToLock <= ReadWriteMutexWriteLockError; ReadWriteMutexReadLockError,
106    FailedToCreate <= ReadWriteMutexCreationError
107}
108
109/// The builder for the [`ReadWriteMutex`].
110#[derive(Debug)]
111pub struct ReadWriteMutexBuilder {
112    is_interprocess_capable: bool,
113}
114
115impl Default for ReadWriteMutexBuilder {
116    fn default() -> Self {
117        ReadWriteMutexBuilder {
118            is_interprocess_capable: true,
119        }
120    }
121}
122
123impl ReadWriteMutexBuilder {
124    pub fn new() -> Self {
125        Self::default()
126    }
127
128    /// Defines if the [`ReadWriteMutex`] is inter-process capable or not.
129    pub fn is_interprocess_capable(mut self, value: bool) -> Self {
130        self.is_interprocess_capable = value;
131        self
132    }
133
134    fn initialize_rw_mutex(
135        &self,
136        mtx: *mut posix::pthread_rwlock_t,
137    ) -> Result<Capability, ReadWriteMutexCreationError> {
138        let msg = "Failed to create mutex";
139        let origin = format!("{:?}", self);
140
141        let mut attributes = ScopeGuardBuilder::new(posix::pthread_rwlockattr_t::new()).on_init(|attr| {
142            handle_errno!(ReadWriteMutexCreationError, from self,
143                errno_source unsafe { posix::pthread_rwlockattr_init( attr).into() },
144                success Errno::ESUCCES => (),
145                Errno::ENOMEM => (InsufficientMemory, "{} due to insufficient memory while creating rwlock attributes.", msg),
146                v => (UnknownError(v as i32), "{} since an unknown error occurred while creating rwlock attributes.", msg)
147            );
148        }).on_drop(|attr| {
149            match unsafe {posix::pthread_rwlockattr_destroy(attr) } {
150                0 => (),
151                v => {
152                    fatal_panic!(from origin, "This should never happen! Failed to release rwlock attributes ({}).", v);
153                }
154            }
155        }).create()?;
156
157        match unsafe { posix::pthread_rwlockattr_setpshared(attributes.get_mut(), 0) } {
158            0 => (),
159            v => {
160                fail!(from origin, with ReadWriteMutexCreationError::NoInterProcessSupport,
161                        "{} due to an unknown error while setting interprocess capabilities ({}).", msg,v );
162            }
163        }
164
165        match unsafe { posix::pthread_rwlock_init(mtx, attributes.get()).into() } {
166            Errno::ESUCCES => (),
167            Errno::EAGAIN => {
168                fail!(from origin, with ReadWriteMutexCreationError::InsufficientResources, "{} due to insufficient resources.", msg);
169            }
170            Errno::ENOMEM => {
171                fail!(from origin, with ReadWriteMutexCreationError::InsufficientResources, "{} due to insufficient memory.", msg);
172            }
173            Errno::EPERM => {
174                fail!(from origin, with ReadWriteMutexCreationError::InsufficientPermissions, "{} due to insufficient permissions.", msg);
175            }
176            v => {
177                fail!(from origin, with ReadWriteMutexCreationError::UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v);
178            }
179        };
180
181        match self.is_interprocess_capable {
182            true => Ok(Capability::InterProcess),
183            false => Ok(Capability::ProcessLocal),
184        }
185    }
186
187    /// Creates a new [`ReadWriteMutex`]
188    pub fn create<T: Debug>(
189        self,
190        t: T,
191        handle: &ReadWriteMutexHandle<T>,
192    ) -> Result<ReadWriteMutex<'_, T>, ReadWriteMutexCreationError> {
193        unsafe {
194            handle
195                .handle
196                .initialize(|mtx| self.initialize_rw_mutex(mtx))?
197        };
198
199        unsafe { *handle.value.get() = Some(t) };
200
201        Ok(ReadWriteMutex::new(handle))
202    }
203}
204
205/// A guard which provides read access to the underlying value of a [`ReadWriteMutex`].
206///
207/// Is returned by [`ReadWriteMutex::read_blocking_lock()`] and [`ReadWriteMutex::read_try_lock()`].
208#[derive(Debug)]
209pub struct MutexReadGuard<'a, 'b, T: Debug> {
210    mutex: &'a ReadWriteMutex<'b, T>,
211}
212
213unsafe impl<T: Send + Debug> Send for MutexReadGuard<'_, '_, T> {}
214unsafe impl<T: Send + Sync + Debug> Sync for MutexReadGuard<'_, '_, T> {}
215
216impl<T: Debug> Deref for MutexReadGuard<'_, '_, T> {
217    type Target = T;
218
219    fn deref(&self) -> &Self::Target {
220        unsafe { (*self.mutex.handle.value.get()).as_ref().unwrap() }
221    }
222}
223
224impl<T: Debug> Drop for MutexReadGuard<'_, '_, T> {
225    fn drop(&mut self) {
226        if self.mutex.release().is_err() {
227            fatal_panic!(from self.mutex, "This should never happen! Failed to release read lock.");
228        }
229    }
230}
231
232/// A guard which provides read and write access to the underlying value of a [`ReadWriteMutex`].
233///
234/// Is returned by [`ReadWriteMutex::write_blocking_lock()`] and [`ReadWriteMutex::write_try_lock()`].
235#[derive(Debug)]
236pub struct MutexWriteGuard<'a, 'b, T: Debug> {
237    mutex: &'a ReadWriteMutex<'b, T>,
238}
239
240unsafe impl<T: Send + Debug> Send for MutexWriteGuard<'_, '_, T> {}
241unsafe impl<T: Send + Sync + Debug> Sync for MutexWriteGuard<'_, '_, T> {}
242
243impl<T: Debug> Deref for MutexWriteGuard<'_, '_, T> {
244    type Target = T;
245
246    fn deref(&self) -> &Self::Target {
247        unsafe { (*self.mutex.handle.value.get()).as_ref().unwrap() }
248    }
249}
250
251impl<T: Debug> DerefMut for MutexWriteGuard<'_, '_, T> {
252    fn deref_mut(&mut self) -> &mut Self::Target {
253        unsafe { (*self.mutex.handle.value.get()).as_mut().unwrap() }
254    }
255}
256
257impl<T: Debug> Drop for MutexWriteGuard<'_, '_, T> {
258    fn drop(&mut self) {
259        if self.mutex.release().is_err() {
260            fatal_panic!(from self.mutex, "This should never happen! Failed to release write lock.");
261        }
262    }
263}
264
265/// The underlying memory of a [`ReadWriteMutex`] is not allowed to be moved. This issue is solved
266/// by storing the underlying posix handle inside [`ReadWriteMutexHandle`]. When a [`ReadWriteMutex`]
267/// is initialized it stores a const reference to the [`ReadWriteMutexHandle`] and makes it by
268/// that inmovable.
269#[derive(Debug)]
270pub struct ReadWriteMutexHandle<T: Sized + Debug> {
271    handle: HandleStorage<posix::pthread_rwlock_t>,
272    value: UnsafeCell<Option<T>>,
273}
274
275unsafe impl<T: Sized + Debug> Send for ReadWriteMutexHandle<T> {}
276unsafe impl<T: Sized + Debug> Sync for ReadWriteMutexHandle<T> {}
277
278impl<T: Sized + Debug> Handle for ReadWriteMutexHandle<T> {
279    fn new() -> Self {
280        Self {
281            handle: HandleStorage::new(posix::pthread_rwlock_t::new()),
282            value: UnsafeCell::new(None),
283        }
284    }
285
286    fn is_initialized(&self) -> bool {
287        self.handle.is_initialized()
288    }
289
290    fn is_inter_process_capable(&self) -> bool {
291        self.handle.is_inter_process_capable()
292    }
293}
294
295impl<T: Sized + Debug> Drop for ReadWriteMutexHandle<T> {
296    fn drop(&mut self) {
297        if self.handle.is_initialized() {
298            unsafe {
299                self.handle.cleanup(|mtx|{
300                    if posix::pthread_rwlock_destroy(mtx) != 0 {
301                        warn!(from self,
302                            "Unable to destroy read write mutex. Was it already destroyed by another instance in another process?");
303                    }
304                });
305            }
306        }
307    }
308}
309
310/// A POSIX read write mutex where either multiple readers can acquire multiple read-locks or one
311/// writer can acquire a write-lock.
312/// It is built by the [`ReadWriteMutexBuilder`].
313#[derive(Debug)]
314pub struct ReadWriteMutex<'a, T: Sized + Debug> {
315    handle: &'a ReadWriteMutexHandle<T>,
316}
317
318unsafe impl<T: Send + Debug> Send for ReadWriteMutex<'_, T> {}
319unsafe impl<T: Sync + Debug> Sync for ReadWriteMutex<'_, T> {}
320
321impl<'a, T: Sized + Debug> IpcConstructible<'a, ReadWriteMutexHandle<T>> for ReadWriteMutex<'a, T> {
322    fn new(handle: &'a ReadWriteMutexHandle<T>) -> Self {
323        Self { handle }
324    }
325}
326
327impl<'a, T: Sized + Debug> IpcCapable<'a, ReadWriteMutexHandle<T>> for ReadWriteMutex<'a, T> {
328    fn is_interprocess_capable(&self) -> bool {
329        self.handle.is_inter_process_capable()
330    }
331}
332
333impl<'a, T: Sized + Debug> ReadWriteMutex<'a, T> {
334    fn new(handle: &'a ReadWriteMutexHandle<T>) -> Self {
335        Self { handle }
336    }
337
338    pub fn read_blocking_lock(
339        &self,
340    ) -> Result<MutexReadGuard<'_, '_, T>, ReadWriteMutexReadLockError> {
341        let msg = "Failed to acquire read-lock";
342        handle_errno!(ReadWriteMutexReadLockError, from self,
343            errno_source unsafe { posix::pthread_rwlock_rdlock(self.handle.handle.get()).into() },
344            success Errno::ESUCCES => MutexReadGuard { mutex: self },
345            Errno::EAGAIN => (MaximumAmountOfReadLocksAcquired, "{} since the maximum amount of read-locks is already acquired.", msg),
346            Errno::EDEADLK => (DeadlockConditionDetected, "{} since a deadlock condition was detected.", msg),
347            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
348        );
349    }
350
351    /// Tries to acquire a read-lock. If a write-locks was already acquired it returns [`None`]
352    /// otherwise a [`MutexReadGuard`].
353    pub fn read_try_lock(
354        &self,
355    ) -> Result<Option<MutexReadGuard<'_, '_, T>>, ReadWriteMutexReadLockError> {
356        let msg = "Failed to try to acquire read-lock";
357        handle_errno!(ReadWriteMutexReadLockError, from self,
358            errno_source unsafe { posix::pthread_rwlock_tryrdlock(self.handle.handle.get()).into() },
359            success Errno::ESUCCES => Some(MutexReadGuard { mutex: self });
360            success Errno::EBUSY => None;
361            success Errno::EDEADLK => None,
362            Errno::EAGAIN => (MaximumAmountOfReadLocksAcquired, "{} since the maximum amount of read-locks is already acquired.", msg),
363            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
364        );
365    }
366
367    /// Blocks until a write-lock could be acquired and returns a [`MutexWriteGuard`] to provide
368    /// read-write access to the underlying value.
369    pub fn write_blocking_lock(
370        &self,
371    ) -> Result<MutexWriteGuard<'_, '_, T>, ReadWriteMutexWriteLockError> {
372        let msg = "Failed to acquire write-lock";
373        handle_errno!(ReadWriteMutexWriteLockError, from self,
374            errno_source unsafe { posix::pthread_rwlock_wrlock(self.handle.handle.get()).into() },
375            success Errno::ESUCCES => MutexWriteGuard { mutex: self },
376            Errno::EDEADLK => (DeadlockConditionDetected, "{} since a deadlock condition was detected.", msg),
377            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
378        );
379    }
380
381    /// Tries to acquire a read-lock. If a read-locks was already acquired it returns [`None`]
382    /// otherwise a [`MutexWriteGuard`].
383    pub fn write_try_lock(
384        &self,
385    ) -> Result<Option<MutexWriteGuard<'_, '_, T>>, ReadWriteMutexWriteLockError> {
386        let msg = "Failed to try to acquire write-lock";
387        handle_errno!(ReadWriteMutexWriteLockError, from self,
388            errno_source unsafe { posix::pthread_rwlock_trywrlock(self.handle.handle.get()).into() },
389            success Errno::ESUCCES => Some(MutexWriteGuard { mutex: self });
390            success Errno::EBUSY => None;
391            success Errno::EDEADLK => None,
392            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
393        );
394    }
395
396    fn release(&self) -> Result<(), ReadWriteMutexUnlockError> {
397        let msg = "Unable to release lock";
398        match unsafe { posix::pthread_rwlock_unlock(self.handle.handle.get()).into() } {
399            Errno::ESUCCES => Ok(()),
400            Errno::EPERM => {
401                fail!(from self, with ReadWriteMutexUnlockError::OwnedByDifferentEntity,
402                    "{} since it is not owned by the current thread.", msg);
403            }
404            v => {
405                fail!(from self, with ReadWriteMutexUnlockError::UnknownError(v as i32),
406                    "{} since an unknown error occurred ({}).", msg, v);
407            }
408        }
409    }
410}