osal_rs/posix/mutex.rs
1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20
21//! Mutex implementations for POSIX, with priority inheritance.
22//!
23//! [`Mutex<T>`] is the data-guarding, RAII-style mutex most application code
24//! should use (see the example below). [`RawMutex`] is the lower-level
25//! primitive it's built on - a bare, lock/unlock pair with no data attached
26//! and no guard - for callers that need to manage locking by hand (e.g. to
27//! pair a mutex with a condition variable, as [`crate::posix::thread`] does).
28//!
29//! Both are backed by a `pthread_mutex_t` configured with
30//! `PTHREAD_PRIO_INHERIT` (a low-priority holder blocking a higher-priority
31//! waiter is temporarily boosted, avoiding priority inversion) and
32//! `PTHREAD_MUTEX_RECURSIVE` (the owning thread may lock it again without
33//! deadlocking, as long as it unlocks the same number of times).
34//!
35//! # Examples
36//!
37//! ```
38//! use osal_rs::os::*;
39//!
40//! let mutex = Mutex::new(0);
41//! {
42//! let mut guard = mutex.lock().unwrap();
43//! *guard += 1;
44//! } // Lock released here
45//!
46//! assert_eq!(*mutex.lock().unwrap(), 1);
47//! ```
48
49use core::cell::UnsafeCell;
50use core::fmt::{Debug, Display, Formatter};
51use core::marker::PhantomData;
52use core::ops::{Deref, DerefMut};
53
54use alloc::sync::Arc;
55
56use crate::posix::ffi::{
57 PTHREAD_MUTEX_RECURSIVE, PTHREAD_PRIO_INHERIT, pthread_mutex_destroy, pthread_mutex_init, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock,
58 pthread_mutexattr_init, pthread_mutexattr_setprotocol, pthread_mutexattr_settype, pthread_mutexattr_t,
59};
60use crate::posix::types::MutexHandle;
61use crate::traits::{MutexFn, MutexGuardFn, RawMutexFn};
62use crate::utils::{Error, OsalRsBool, Result};
63
64/// Low-level POSIX mutex: lock/unlock only, no guarded data and no RAII
65/// guard. Most application code should use [`Mutex<T>`] instead; `RawMutex`
66/// is for callers that need to manage the lock/unlock pairing themselves.
67pub struct RawMutex(UnsafeCell<MutexHandle>);
68
69unsafe impl Send for RawMutex {}
70unsafe impl Sync for RawMutex {}
71
72impl RawMutex {
73 /// Creates a new, unlocked mutex with priority inheritance enabled.
74 ///
75 /// # Examples
76 ///
77 /// ```
78 /// use osal_rs::os::*;
79 /// use osal_rs::utils::OsalRsBool;
80 ///
81 /// let mutex = RawMutex::new().unwrap();
82 /// assert_eq!(mutex.lock(), OsalRsBool::True);
83 /// assert_eq!(mutex.unlock(), OsalRsBool::True);
84 /// ```
85 pub fn new() -> Result<Self> {
86 let mut mutex_attr: pthread_mutexattr_t = Default::default();
87 let mut mutex: MutexHandle = Default::default();
88
89 unsafe {
90 pthread_mutexattr_init(&mut mutex_attr);
91 pthread_mutexattr_setprotocol(&mut mutex_attr, PTHREAD_PRIO_INHERIT);
92 pthread_mutexattr_settype(&mut mutex_attr, PTHREAD_MUTEX_RECURSIVE);
93 }
94
95 let result = unsafe { pthread_mutex_init(&mut mutex, &mutex_attr) };
96
97 if result != 0 {
98 return Err(Error::ReturnWithCode(result));
99 }
100
101 Ok(Self(UnsafeCell::new(mutex)))
102 }
103}
104
105impl RawMutexFn for RawMutex {
106
107 /// Returns `true` if this mutex is never-initialized-or-already-deleted.
108 ///
109 /// # Examples
110 ///
111 /// ```
112 /// use osal_rs::os::*;
113 ///
114 /// let mut mutex = RawMutex::new().unwrap();
115 /// assert!(!mutex.is_null());
116 ///
117 /// mutex.delete();
118 /// assert!(mutex.is_null());
119 /// ```
120 fn is_null(&self) -> bool {
121 unsafe { (*self.0.get()).is_empty() }
122 }
123
124 /// Locks the mutex, blocking the calling thread until it becomes
125 /// available. Returns [`OsalRsBool::True`] on success.
126 ///
127 /// # Examples
128 ///
129 /// ```
130 /// use osal_rs::os::*;
131 /// use osal_rs::utils::OsalRsBool;
132 ///
133 /// let mutex = RawMutex::new().unwrap();
134 /// assert_eq!(mutex.lock(), OsalRsBool::True);
135 /// mutex.unlock();
136 /// ```
137 fn lock(&self) -> OsalRsBool {
138 if self.is_null() {
139 return OsalRsBool::False;
140 }
141
142 match unsafe { pthread_mutex_lock(self.0.get()) } {
143 0 => OsalRsBool::True,
144 _ => OsalRsBool::False,
145 }
146 }
147
148 /// ISR-safe variant of [`RawMutex::lock`]. POSIX has no interrupt
149 /// context of its own, so this never blocks (`trylock` instead of
150 /// `lock`); it returns [`OsalRsBool::False`] if the mutex is already
151 /// held rather than waiting for it.
152 ///
153 /// # Examples
154 ///
155 /// ```
156 /// use osal_rs::os::*;
157 /// use osal_rs::utils::OsalRsBool;
158 ///
159 /// let mutex = RawMutex::new().unwrap();
160 /// assert_eq!(mutex.lock_from_isr(), OsalRsBool::True);
161 /// mutex.unlock_from_isr();
162 /// ```
163 fn lock_from_isr(&self) -> OsalRsBool {
164 if self.is_null() {
165 return OsalRsBool::False;
166 }
167
168 // pthreads has no ISR context/API of its own; `trylock` gives the
169 // non-blocking behavior `lock_from_isr` callers expect instead.
170 match unsafe { pthread_mutex_trylock(self.0.get()) } {
171 0 => OsalRsBool::True,
172 _ => OsalRsBool::False,
173 }
174 }
175
176 /// Unlocks the mutex. Must be called by the thread that currently holds
177 /// the lock.
178 ///
179 /// # Examples
180 ///
181 /// ```
182 /// use osal_rs::os::*;
183 /// use osal_rs::utils::OsalRsBool;
184 ///
185 /// let mutex = RawMutex::new().unwrap();
186 /// mutex.lock();
187 /// assert_eq!(mutex.unlock(), OsalRsBool::True);
188 /// ```
189 fn unlock(&self) -> OsalRsBool {
190 if self.is_null() {
191 return OsalRsBool::False;
192 }
193
194 match unsafe { pthread_mutex_unlock(self.0.get()) } {
195 0 => OsalRsBool::True,
196 _ => OsalRsBool::False,
197 }
198 }
199
200 /// ISR-safe variant of [`RawMutex::unlock`]; identical on POSIX, since
201 /// unlocking never blocks.
202 fn unlock_from_isr(&self) -> OsalRsBool {
203 self.unlock()
204 }
205
206 /// Destroys the underlying pthread mutex and resets it to its "null"
207 /// state. Safe to call more than once, and called automatically on
208 /// [`Drop`] if not called explicitly.
209 ///
210 /// # Examples
211 ///
212 /// ```
213 /// use osal_rs::os::*;
214 ///
215 /// let mut mutex = RawMutex::new().unwrap();
216 /// mutex.delete();
217 /// assert!(mutex.is_null());
218 /// ```
219 fn delete(&mut self) {
220 if self.is_null() {
221 return;
222 }
223
224 unsafe {
225 pthread_mutex_destroy(self.0.get());
226 }
227
228 *self.0.get_mut() = MutexHandle::default();
229 }
230}
231
232impl Drop for RawMutex {
233 fn drop(&mut self) {
234 self.delete();
235 }
236}
237
238impl Deref for RawMutex {
239 type Target = MutexHandle;
240
241 fn deref(&self) -> &MutexHandle {
242 unsafe { &*self.0.get() }
243 }
244}
245
246impl Debug for RawMutex {
247 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
248
249 f.debug_struct("RawMutex")
250 .field("handle", &(&raw const self).addr())
251 .finish()
252 }
253}
254
255impl Display for RawMutex {
256 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
257 write!(f, "RawMutex {{ handle: {:?} }}", &(&raw const self).addr())
258 }
259}
260
261/// A mutex protecting a `T`, unlocked through RAII guards rather than
262/// explicit lock/unlock calls. Built on [`RawMutex`], so it shares the same
263/// priority-inheritance and recursive-locking behavior.
264///
265/// # Examples
266///
267/// ```
268/// use osal_rs::os::*;
269///
270/// let mutex = Mutex::new(0);
271/// {
272/// let mut guard = mutex.lock().unwrap();
273/// *guard += 1;
274/// } // Lock released here, when `guard` drops.
275///
276/// assert_eq!(*mutex.lock().unwrap(), 1);
277/// ```
278pub struct Mutex<T: ?Sized> {
279 inner: RawMutex,
280 data: UnsafeCell<T>,
281}
282
283unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
284unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
285
286impl<T: ?Sized> Mutex<T> {
287 /// Wraps `data` in a new, unlocked mutex.
288 ///
289 /// # Examples
290 ///
291 /// ```
292 /// use osal_rs::os::*;
293 ///
294 /// let mutex = Mutex::new(vec![1, 2, 3]);
295 /// assert_eq!(mutex.lock().unwrap().len(), 3);
296 /// ```
297 pub fn new(data: T) -> Self
298 where
299 T: Sized,
300 {
301 Self {
302 inner: RawMutex::new().unwrap(),
303 data: UnsafeCell::new(data),
304 }
305 }
306
307 #[inline]
308 fn get_mut_ref(&mut self) -> &mut T {
309 unsafe { &mut *self.data.get() }
310 }
311}
312
313impl<T: ?Sized> MutexFn<T> for Mutex<T> {
314 type Guard<'a> = MutexGuard<'a, T> where Self: 'a, T: 'a;
315 type GuardFromIsr<'a> = MutexGuardFromIsr<'a, T> where Self: 'a, T: 'a;
316
317 /// Blocks the calling thread until the mutex is available, then returns
318 /// a RAII guard that unlocks it on [`Drop`].
319 ///
320 /// # Examples
321 ///
322 /// ```
323 /// use osal_rs::os::*;
324 ///
325 /// let mutex = Mutex::new(10);
326 /// let mut guard = mutex.lock().unwrap();
327 /// *guard += 5;
328 /// drop(guard);
329 ///
330 /// assert_eq!(*mutex.lock().unwrap(), 15);
331 /// ```
332 fn lock(&self) -> Result<Self::Guard<'_>> {
333 match self.inner.lock() {
334 OsalRsBool::True => Ok(MutexGuard {
335 mutex: self,
336 _phantom: PhantomData,
337 }),
338 OsalRsBool::False => Err(Error::MutexLockFailed),
339 }
340 }
341
342 /// ISR-safe variant of [`Mutex::lock`]: never blocks, failing with
343 /// [`Error::MutexLockFailed`] instead of waiting if the mutex is already
344 /// held (POSIX has no real interrupt context of its own, so this is the
345 /// non-blocking equivalent expected of `_from_isr` methods).
346 ///
347 /// # Examples
348 ///
349 /// ```
350 /// use osal_rs::os::*;
351 ///
352 /// let mutex = Mutex::new(0);
353 /// let mut guard = mutex.lock_from_isr().unwrap();
354 /// *guard = 42;
355 /// drop(guard);
356 ///
357 /// assert_eq!(*mutex.lock().unwrap(), 42);
358 /// ```
359 fn lock_from_isr(&self) -> Result<Self::GuardFromIsr<'_>> {
360 match self.inner.lock_from_isr() {
361 OsalRsBool::True => Ok(MutexGuardFromIsr {
362 mutex: self,
363 _phantom: PhantomData,
364 }),
365 OsalRsBool::False => Err(Error::MutexLockFailed),
366 }
367 }
368
369 /// Consumes the mutex, returning the wrapped value without needing to
370 /// lock it (the type system already guarantees exclusive access here).
371 ///
372 /// # Examples
373 ///
374 /// ```
375 /// use osal_rs::os::*;
376 ///
377 /// let mutex = Mutex::new(String::from("hello"));
378 /// let value = mutex.into_inner().unwrap();
379 /// assert_eq!(value, "hello");
380 /// ```
381 fn into_inner(self) -> Result<T>
382 where
383 Self: Sized,
384 T: Sized,
385 {
386 Ok(self.data.into_inner())
387 }
388
389 /// Returns a mutable reference to the wrapped value without locking (a
390 /// `&mut Mutex<T>` already guarantees exclusive access).
391 ///
392 /// # Examples
393 ///
394 /// ```
395 /// use osal_rs::os::*;
396 ///
397 /// let mut mutex = Mutex::new(1);
398 /// *mutex.get_mut() += 1;
399 /// assert_eq!(*mutex.lock().unwrap(), 2);
400 /// ```
401 fn get_mut(&mut self) -> &mut T {
402 self.get_mut_ref()
403 }
404}
405
406impl<T: ?Sized> Mutex<T> {
407 /// Same as [`MutexFn::lock_from_isr`], exposed as an inherent method so
408 /// it's callable without importing the [`MutexFn`] trait.
409 ///
410 /// # Examples
411 ///
412 /// ```
413 /// use osal_rs::os::*;
414 ///
415 /// let mutex = Mutex::new(0);
416 /// let mut guard = mutex.lock_from_isr_explicit().unwrap();
417 /// *guard = 7;
418 /// ```
419 pub fn lock_from_isr_explicit(&self) -> Result<MutexGuardFromIsr<'_, T>> {
420 match self.inner.lock_from_isr() {
421 OsalRsBool::True => Ok(MutexGuardFromIsr {
422 mutex: self,
423 _phantom: PhantomData,
424 }),
425 OsalRsBool::False => Err(Error::MutexLockFailed),
426 }
427 }
428}
429
430impl<T> Mutex<T> {
431 /// Convenience constructor for the common case of sharing a mutex
432 /// between threads: equivalent to `Arc::new(Mutex::new(data))`.
433 ///
434 /// # Examples
435 ///
436 /// ```
437 /// use osal_rs::os::*;
438 ///
439 /// let shared = Mutex::new_arc(0);
440 /// let clone_for_worker = shared.clone();
441 ///
442 /// *clone_for_worker.lock().unwrap() += 1;
443 /// assert_eq!(*shared.lock().unwrap(), 1);
444 /// ```
445 pub fn new_arc(data: T) -> Arc<Self> {
446 Arc::new(Self::new(data))
447 }
448}
449
450impl<T: ?Sized> Debug for Mutex<T> {
451 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
452 f.debug_struct("Mutex")
453 .field("inner", &self.inner)
454 .finish()
455 }
456}
457
458impl<T: ?Sized> Display for Mutex<T> {
459 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
460 write!(f, "Mutex {{ inner: {} }}", self.inner)
461 }
462}
463
464/// RAII guard returned by [`Mutex::lock`]. Unlocks the mutex when dropped;
465/// derefs to `&T`/`&mut T` in the meantime.
466pub struct MutexGuard<'a, T: ?Sized + 'a> {
467 mutex: &'a Mutex<T>,
468 _phantom: PhantomData<&'a mut T>,
469}
470
471impl<'a, T: ?Sized> MutexGuard<'a, T> {
472 /// Raw handle of the pthread mutex backing this guard.
473 ///
474 /// Lets a condition variable (`pthread_cond_wait`/`pthread_cond_timedwait`)
475 /// atomically unlock/re-lock the same OS mutex this guard represents,
476 /// without going through [`RawMutexFn::unlock`]/`lock` — those calls are
477 /// made internally by libc during the wait, so the guard's Rust-level
478 /// "locked" state stays valid across it.
479 pub(crate) fn raw_handle(&self) -> *mut MutexHandle {
480 self.mutex.inner.0.get()
481 }
482}
483
484impl<'a, T: ?Sized> Deref for MutexGuard<'a, T> {
485 type Target = T;
486
487 fn deref(&self) -> &T {
488 unsafe { &*self.mutex.data.get() }
489 }
490}
491
492impl<'a, T: ?Sized> DerefMut for MutexGuard<'a, T> {
493 fn deref_mut(&mut self) -> &mut T {
494 unsafe { &mut *self.mutex.data.get() }
495 }
496}
497
498impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> {
499 fn drop(&mut self) {
500 let _ = self.mutex.inner.unlock();
501 }
502}
503
504impl<'a, T: ?Sized> MutexGuardFn<'a, T> for MutexGuard<'a, T> {
505 /// Replaces the guarded value with a clone of `t`, without needing a
506 /// separate `*guard = t.clone()` statement.
507 ///
508 /// # Examples
509 ///
510 /// ```
511 /// use osal_rs::os::*;
512 ///
513 /// let mutex = Mutex::new(0);
514 /// let mut guard = mutex.lock().unwrap();
515 /// guard.update(&42);
516 /// assert_eq!(*guard, 42);
517 /// ```
518 fn update(&mut self, t: &T)
519 where
520 T: Clone,
521 {
522 **self = t.clone();
523 }
524}
525
526/// RAII guard returned by [`Mutex::lock_from_isr`]/[`Mutex::lock_from_isr_explicit`].
527/// Unlocks the mutex (via the non-blocking `_from_isr` path) when dropped;
528/// derefs to `&T`/`&mut T` in the meantime.
529pub struct MutexGuardFromIsr<'a, T: ?Sized + 'a> {
530 mutex: &'a Mutex<T>,
531 _phantom: PhantomData<&'a mut T>,
532}
533
534impl<'a, T: ?Sized> Deref for MutexGuardFromIsr<'a, T> {
535 type Target = T;
536
537 fn deref(&self) -> &T {
538 unsafe { &*self.mutex.data.get() }
539 }
540}
541
542impl<'a, T: ?Sized> DerefMut for MutexGuardFromIsr<'a, T> {
543 fn deref_mut(&mut self) -> &mut T {
544 unsafe { &mut *self.mutex.data.get() }
545 }
546}
547
548impl<'a, T: ?Sized> Drop for MutexGuardFromIsr<'a, T> {
549 fn drop(&mut self) {
550 let _ = self.mutex.inner.unlock_from_isr();
551 }
552}
553
554impl<'a, T: ?Sized> MutexGuardFn<'a, T> for MutexGuardFromIsr<'a, T> {
555 /// See [`MutexGuard::update`]; behaves identically here.
556 ///
557 /// # Examples
558 ///
559 /// ```
560 /// use osal_rs::os::*;
561 ///
562 /// let mutex = Mutex::new(0);
563 /// let mut guard = mutex.lock_from_isr().unwrap();
564 /// guard.update(&7);
565 /// assert_eq!(*guard, 7);
566 /// ```
567 fn update(&mut self, t: &T)
568 where
569 T: Clone,
570 {
571 **self = t.clone();
572 }
573}