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}