Skip to main content

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}