Skip to main content

posix_sync/mutex/
borrowed.rs

1use std::cell::UnsafeCell;
2use std::fmt::{self, Debug};
3use std::marker::{PhantomData, Send, Sync, Unpin};
4#[cfg(not(target_vendor = "apple"))]
5use std::time::Duration;
6
7use libc::pthread_mutex_t;
8
9use super::errors::MutexLockError;
10use super::robustness_markers::RobustnessMarker;
11use super::{AsRawUnderlying, RawMutexAlloc};
12#[cfg(not(target_vendor = "apple"))]
13use crate::utils::deadline_from_now;
14use crate::utils::Sealed;
15
16/// A mutex that borrows the memory for its underlying mutex. The underlying mutex will **not** be
17/// destroyed when `BorrowedMutex` is dropped. It is the caller's responsibility to call
18/// [`destroy`](BorrowedMutex::destroy) when the mutex is no longer needed.
19///
20/// # Safety
21///
22/// The methods of `BorrowedMutex`, unlike those of [`OwnedMutex`](crate::mutex::OwnedMutex), are
23/// unsafe. This is because the underlying mutex may be in a shared memory mapping, where it may
24/// be modifiable by other processes, and calling these methods can lead to undefined behaviour if
25/// certain invariants are not upheld. Here is a non-exhaustive list of some of the situations that
26/// will lead to undefined behaviour:
27///
28/// - Another process destroys the mutex before your process calls `destroy`. Calling any method on
29///   the mutex (even destroy itself) is now UB.
30/// - Another process unlocks the mutex without holding a lock.
31/// - A process owns a lock and tries to lock the mutex again (unless the mutex is error-checked).
32///
33/// For a more thorough description of what situations can result in UB, read some of the `pthread_mutex_*`
34/// pages in the [POSIX standard](https://pubs.opengroup.org/onlinepubs/9799919799/idx/ip.html).
35pub struct BorrowedMutex<'a, R: RobustnessMarker> {
36    raw: *mut RawMutexAlloc,
37
38    /// The `*const UnsafeCell` is to prevent the type from being `UnwindSafe` and `RefUnwindSafe`.
39    _phantom: PhantomData<(*const UnsafeCell<R>, &'a ())>,
40}
41
42impl<R: RobustnessMarker> Sealed for BorrowedMutex<'_, R> {}
43unsafe impl<R: RobustnessMarker> Send for BorrowedMutex<'_, R> {}
44unsafe impl<R: RobustnessMarker> Sync for BorrowedMutex<'_, R> {}
45impl<R: RobustnessMarker> Unpin for BorrowedMutex<'_, R> {}
46
47impl<R: RobustnessMarker> Clone for BorrowedMutex<'_, R> {
48    #[inline]
49    fn clone(&self) -> Self {
50        *self
51    }
52}
53
54impl<R: RobustnessMarker> Copy for BorrowedMutex<'_, R> {}
55
56impl<R: RobustnessMarker> AsRawUnderlying for BorrowedMutex<'_, R> {
57    type Underlying = pthread_mutex_t;
58
59    fn as_raw_underlying(&self) -> *mut pthread_mutex_t {
60        self.raw as *mut _
61    }
62}
63
64impl<R: RobustnessMarker> Debug for BorrowedMutex<'_, R> {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        f.debug_struct("BorrowedMutex")
67            .field("raw", &self.raw)
68            .finish()
69    }
70}
71
72impl<R: RobustnessMarker> BorrowedMutex<'_, R> {
73    /// Constructs a `BorrowedMutex` from an existing, initialised underlying mutex at a given
74    /// location in memory.
75    ///
76    /// - `raw`: A pointer to an existing raw mutex. Note that, unlike
77    ///   [`MutexBuilder::build_borrowed`](crate::mutex::MutexBuilder::build_borrowed),
78    ///   this method expects that the pointee is initialised.
79    ///
80    /// - `_memory`: A reference to a RAII object whose lifetime determines the validity of
81    ///   `raw` (e.g. a struct that manages a memory map).
82    ///
83    /// # Safety
84    /// The caller must guarantee that `raw` points to a valid, initialised [`RawMutexAlloc`]
85    /// (a.k.a. `pthread_mutex_t`) whose robustness attribute matches `R`. Getting the robustness
86    /// wrong does not corrupt anything by itself, but it does mean the guard type is lying about
87    /// which outcomes are possible.
88    ///
89    /// Also, read the type-level safety section.
90    #[inline]
91    pub unsafe fn from_raw<T>(raw: *mut RawMutexAlloc, _memory: &T) -> BorrowedMutex<'_, R> {
92        BorrowedMutex {
93            raw,
94            _phantom: PhantomData,
95        }
96    }
97
98    /// Calls [`pthread_mutex_destroy`](https://man7.org/linux/man-pages/man3/pthread_mutex_destroy.3p.html)
99    /// on the underlying mutex. It is safe to re-initialise another mutex at this address
100    /// afterwards.
101    ///
102    /// Failure to call this function before destroying the mutex can result in resource leaks,
103    /// but will not lead to undefined behaviour.
104    ///
105    /// # Safety
106    /// The caller must ensure that there is no way for the mutex
107    /// to be locked by any thread (from any process) while the call is taking place. Notably, this
108    /// means the mutex can not be in the middle of a wait call to a condvar.
109    ///
110    /// Moreover, the caller must ensure that destroy is only called once.
111    ///
112    /// Attempting to use the mutex after it has been destroyed or destroying the same mutex twice
113    /// is undefined behaviour. You can, however, safely create a new mutex at the same address
114    /// after destroying the old one.
115    ///
116    /// Also, read the type-level safety section.
117    #[inline]
118    pub unsafe fn destroy(self) {
119        unsafe {
120            let r = libc::pthread_mutex_destroy(self.as_raw_underlying());
121            debug_assert_eq!(r, 0);
122        }
123    }
124
125    /// Attempts to lock the mutex in a non-blocking manner. If a lock was obtained, the `Some`
126    /// variant is returned. If a lock could not be obtained but otherwise no error occurred, the
127    /// `None` variant is returned. Otherwise, an error is returned.
128    ///
129    /// # Safety
130    /// This method is unsafe because the backing mutex may be in shared memory and modifiable by
131    /// many processes, where it can potentially be left in an invalid state. As long as processes
132    /// only modify the mutex through libc's `pthread*` functions and don't cause any undefined
133    /// behaviour on their own such as double unlocking or double locking (unless the mutex is
134    /// recursive), it should generally be safe to call this function.
135    ///
136    /// Also, read the type-level safety section.
137    ///
138    /// # Errors
139    /// See [`MutexLockError`]
140    #[inline]
141    pub unsafe fn try_lock(&self) -> Result<Option<R::Guard<'_>>, MutexLockError> {
142        match libc::pthread_mutex_trylock(self.as_raw_underlying()) {
143            libc::EBUSY => Ok(None),
144            e => R::guard_from_libc_returnval(self, e).map(Some),
145        }
146    }
147
148    /// Attempts to lock the mutex, blocking until a lock is obtained.
149    ///
150    /// # Safety
151    /// This method is unsafe because the backing mutex may be in shared memory and modifiable by
152    /// many processes, where it can potentially be left in an invalid state. As long as processes
153    /// only modify the mutex through libc's `pthread*` functions and don't cause any undefined
154    /// behaviour on their own such as double unlocking or double locking (unless the mutex is
155    /// recursive), it should generally be safe to call this function.
156    ///
157    /// Also, read the type-level safety section.
158    ///
159    /// # Errors
160    /// See [`MutexLockError`]
161    #[inline]
162    pub unsafe fn lock(&self) -> Result<R::Guard<'_>, MutexLockError> {
163        R::guard_from_libc_returnval(self, libc::pthread_mutex_lock(self.as_raw_underlying()))
164    }
165
166    /// Attempts to lock the mutex, blocking until a lock is obtained or `timeout` elapses. A lock
167    /// that could not be obtained in time is reported as `Ok(None)` rather than as an error.
168    ///
169    /// The timeout is resolved against `CLOCK_REALTIME`, which is the clock
170    /// [`pthread_mutex_timedlock`](https://man7.org/linux/man-pages/man3/pthread_mutex_timedlock.3p.html)
171    /// is defined in terms of. Stepping the system clock therefore moves the deadline.
172    ///
173    /// Not available on Apple platforms, which do not implement `pthread_mutex_timedlock`.
174    ///
175    /// # Safety
176    /// The same conditions as for [`lock`](Self::lock) apply.
177    ///
178    /// # Errors
179    /// See [`MutexLockError`]
180    #[cfg_attr(docsrs, doc(cfg(not(target_vendor = "apple"))))]
181    #[cfg(not(target_vendor = "apple"))]
182    #[inline]
183    pub unsafe fn lock_for(
184        &self,
185        timeout: Duration,
186    ) -> Result<Option<R::Guard<'_>>, MutexLockError> {
187        let deadline = deadline_from_now(libc::CLOCK_REALTIME, timeout);
188        match libc::pthread_mutex_timedlock(self.as_raw_underlying(), &deadline) {
189            libc::ETIMEDOUT => Ok(None),
190            e => R::guard_from_libc_returnval(self, e).map(Some),
191        }
192    }
193
194    /// Returns a pointer to the underlying `pthread_mutex_t`.
195    #[inline]
196    pub fn as_raw_mutex(&self) -> *mut pthread_mutex_t {
197        self.as_raw_underlying()
198    }
199}