osal_rs/freertos/mutex.rs
1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2023/2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 ***************************************************************************/
19
20//! Mutex synchronization primitives for FreeRTOS.
21//!
22//! This module provides safe mutual exclusion primitives built on top of FreeRTOS
23//! recursive mutexes. It supports RAII-style lock guards for automatic lock management
24//! and ISR-safe variants for interrupt contexts.
25
26use core::cell::UnsafeCell;
27use core::fmt::{Debug, Display, Formatter};
28use core::ops::{Deref, DerefMut};
29use core::marker::PhantomData;
30
31use alloc::sync::Arc;
32
33use super::ffi::{MutexHandle, pdFALSE, pdTRUE};
34use super::system::System;
35use crate::os::StaticMutexFn;
36use crate::traits::SystemFn;
37use crate::traits::{MutexGuardFn, RawMutexFn, MutexFn, ToTick};
38use crate::utils::{Result, Error, OsalRsBool, MAX_DELAY};
39use crate::{vSemaphoreDelete, xSemaphoreCreateRecursiveMutex, xSemaphoreGiveFromISR, xSemaphoreGiveRecursive, xSemaphoreTake, xSemaphoreTakeFromISR, xSemaphoreTakeRecursive};
40
41//// RawMutex ////
42
43/// Low-level recursive mutex wrapper for FreeRTOS.
44///
45/// This is the underlying implementation of the mutex that directly interfaces
46/// with FreeRTOS semaphore APIs. It's recursive, meaning the same thread can
47/// lock it multiple times.
48///
49/// # Note
50///
51/// Users should typically use [`Mutex<T>`] instead, which provides type-safe
52/// data protection. This type is exposed for advanced use cases.
53struct RawMutex(MutexHandle);
54
55unsafe impl Send for RawMutex {}
56unsafe impl Sync for RawMutex {}
57
58impl RawMutexFn for RawMutex {
59 fn new() -> Result<Self> {
60 let handle = xSemaphoreCreateRecursiveMutex!();
61 if handle.is_null() {
62 Err(Error::OutOfMemory)
63 } else {
64 Ok(RawMutex(handle))
65 }
66 }
67
68 fn lock(&self) -> OsalRsBool {
69 let res = xSemaphoreTakeRecursive!(self.0, MAX_DELAY.to_ticks());
70 if res == pdTRUE {
71 OsalRsBool::True
72 } else {
73 OsalRsBool::False
74 }
75 }
76
77 fn lock_from_isr(&self) -> OsalRsBool {
78 let mut higher_priority_task_woken = pdFALSE;
79 let res = xSemaphoreTakeFromISR!(self.0, &mut higher_priority_task_woken);
80 if res == pdTRUE {
81
82 System::yield_from_isr(higher_priority_task_woken);
83
84 OsalRsBool::True
85 } else {
86 OsalRsBool::False
87 }
88 }
89
90 fn unlock(&self) -> OsalRsBool {
91 let res = xSemaphoreGiveRecursive!(self.0);
92 if res == pdTRUE {
93 OsalRsBool::True
94 } else {
95 OsalRsBool::False
96 }
97 }
98
99
100 fn unlock_from_isr(&self) -> OsalRsBool {
101 let mut higher_priority_task_woken = pdFALSE;
102 let res = xSemaphoreGiveFromISR!(self.0, &mut higher_priority_task_woken);
103 if res == pdTRUE {
104
105 System::yield_from_isr(higher_priority_task_woken);
106
107 OsalRsBool::True
108 } else {
109 OsalRsBool::False
110 }
111 }
112
113 fn delete(&mut self) {
114 vSemaphoreDelete!(self.0);
115 self.0 = core::ptr::null();
116 }
117}
118
119impl Drop for RawMutex {
120 fn drop(&mut self) {
121 if self.0.is_null() {
122 return;
123 }
124 self.delete();
125 }
126}
127
128impl Deref for RawMutex {
129 type Target = MutexHandle;
130
131 fn deref(&self) -> &MutexHandle {
132 &self.0
133 }
134}
135
136
137impl Debug for RawMutex {
138 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
139 f.debug_struct("RawMutex")
140 .field("handle", &self.0)
141 .finish()
142 }
143}
144
145impl Display for RawMutex {
146 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
147 write!(f, "RawMutex {{ handle: {:?} }}", self.0)
148 }
149}
150
151//// Mutex ////
152
153/// A mutual exclusion primitive useful for protecting shared data.
154///
155/// This mutex will block threads waiting for the lock to become available.
156/// The mutex is implemented using FreeRTOS recursive mutexes, supporting
157/// priority inheritance to prevent priority inversion.
158///
159/// # Type Parameters
160///
161/// * `T` - The type of data protected by the mutex
162///
163/// # Examples
164///
165/// ## Basic usage
166///
167/// ```ignore
168/// use osal_rs::os::Mutex;
169///
170/// let mutex = Mutex::new(0);
171///
172/// // Acquire the lock and modify the data
173/// {
174/// let mut guard = mutex.lock().unwrap();
175/// *guard += 1;
176/// } // Lock is automatically released here
177/// ```
178///
179/// ## Sharing between threads
180///
181/// ```ignore
182/// use osal_rs::os::{Mutex, Thread};
183/// use alloc::sync::Arc;
184///
185/// let counter = Arc::new(Mutex::new(0));
186/// let counter_clone = counter.clone();
187///
188/// let thread = Thread::new("worker", 2048, 5, move || {
189/// let mut guard = counter_clone.lock().unwrap();
190/// *guard += 1;
191/// }).unwrap();
192///
193/// thread.start().unwrap();
194/// ```
195///
196/// ## Using from ISR context
197///
198/// ```ignore
199/// use osal_rs::os::Mutex;
200///
201/// let mutex = Mutex::new(0);
202///
203/// // In an interrupt handler:
204/// if let Ok(mut guard) = mutex.lock_from_isr() {
205/// *guard = 42;
206/// }
207/// ```
208pub struct Mutex<T: ?Sized> {
209 inner: RawMutex,
210 data: UnsafeCell<T>
211}
212
213
214unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
215unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
216
217impl<T: ?Sized> MutexFn<T> for Mutex<T> {
218 type Guard<'a> = MutexGuard<'a, T> where Self: 'a, T: 'a;
219 type GuardFromIsr<'a> = MutexGuardFromIsr<'a, T> where Self: 'a, T: 'a;
220
221 fn new(data: T) -> Self
222 where
223 T: Sized
224 {
225 Self {
226 inner: RawMutex::new().unwrap(),
227 data: UnsafeCell::new(data),
228 }
229 }
230
231 fn lock(&self) -> Result<Self::Guard<'_>> {
232 match self.inner.lock() {
233 OsalRsBool::True => Ok(MutexGuard {
234 mutex: self,
235 _phantom: PhantomData,
236 }),
237 OsalRsBool::False => Err(Error::MutexLockFailed),
238 }
239 }
240
241 fn lock_from_isr(&self) -> Result<Self::GuardFromIsr<'_>> {
242 match self.inner.lock_from_isr() {
243 OsalRsBool::True => Ok(MutexGuardFromIsr {
244 mutex: self,
245 _phantom: PhantomData,
246 }),
247 OsalRsBool::False => Err(Error::MutexLockFailed),
248 }
249 }
250
251 /// Consumes the mutex and returns the inner data.
252 ///
253 /// This is safe because we have unique ownership of the mutex.
254 ///
255 /// # Examples
256 ///
257 /// ```ignore
258 /// use osal_rs::os::{Mutex, MutexFn};
259 ///
260 /// let mutex = Mutex::new(5);
261 /// let value = mutex.into_inner().unwrap();
262 /// assert_eq!(value, 5);
263 /// ```
264 fn into_inner(self) -> Result<T>
265 where
266 Self: Sized,
267 T: Sized
268 {
269 Ok(self.data.into_inner())
270 }
271
272 /// Returns a mutable reference to the inner data.
273 ///
274 /// Since this takes `&mut self`, we know there are no other references
275 /// to the data, so we can safely return a mutable reference.
276 ///
277 /// # Examples
278 ///
279 /// ```ignore
280 /// use osal_rs::os::{Mutex, MutexFn};
281 ///
282 /// let mut mutex = Mutex::new(0);
283 /// *mutex.get_mut() = 10;
284 /// assert_eq!(*mutex.get_mut(), 10);
285 /// ```
286 fn get_mut(&mut self) -> &mut T {
287 self.data.get_mut()
288 }
289}
290
291impl<T: ?Sized> Mutex<T> {
292 /// Acquires the mutex from ISR context, returning a specific ISR guard.
293 ///
294 /// This is an explicit version of `lock_from_isr` that returns the ISR-specific guard type.
295 ///
296 /// # Returns
297 ///
298 /// * `Ok(MutexGuardFromIsr)` - Lock acquired
299 /// * `Err(Error::MutexLockFailed)` - Failed to acquire lock
300 ///
301 /// # Examples
302 ///
303 /// ```ignore
304 /// // In ISR context:
305 /// if let Ok(guard) = mutex.lock_from_isr_explicit() {
306 /// *guard = new_value;
307 /// }
308 /// ```
309 pub fn lock_from_isr_explicit(&self) -> Result<MutexGuardFromIsr<'_, T>> {
310 match self.inner.lock_from_isr() {
311 OsalRsBool::True => Ok(MutexGuardFromIsr {
312 mutex: self,
313 _phantom: PhantomData,
314 }),
315 OsalRsBool::False => Err(Error::MutexLockFailed),
316 }
317 }
318}
319
320impl<T> Mutex<T> {
321 /// Creates a new mutex wrapped in an Arc for easy sharing between threads.
322 ///
323 /// This is a convenience method equivalent to `Arc::new(Mutex::new(data))`.
324 ///
325 /// # Examples
326 ///
327 /// ```ignore
328 /// use osal_rs::os::Mutex;
329 /// use alloc::sync::Arc;
330 ///
331 /// let shared_data = Mutex::new_arc(0u32);
332 /// let data_clone = Arc::clone(&shared_data);
333 ///
334 /// // Use in thread...
335 /// let thread = Thread::new("worker", 2048, 5, move || {
336 /// let mut guard = data_clone.lock().unwrap();
337 /// *guard += 1;
338 /// });
339 /// ```
340 pub fn new_arc(data: T) -> Arc<Self> {
341 Arc::new(Self::new(data))
342 }
343}
344
345impl<T> Debug for Mutex<T>
346where
347 T: ?Sized {
348 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
349 f.debug_struct("Mutex")
350 .field("inner", &self.inner)
351 .finish()
352 }
353}
354
355impl<T> Display for Mutex<T>
356where
357 T: ?Sized {
358 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
359 write!(f, "Mutex {{ inner: {} }}", self.inner)
360 }
361}
362
363//// MutexGuard ////
364
365/// RAII guard returned by `Mutex::lock()`.
366///
367/// When this guard goes out of scope, the mutex is automatically unlocked.
368/// Provides access to the protected data through `Deref` and `DerefMut`.
369///
370/// # Examples
371///
372/// ```ignore
373/// use osal_rs::os::{Mutex, MutexFn};
374///
375/// let mutex = Mutex::new(0);
376///
377/// {
378/// let mut guard = mutex.lock().unwrap();
379/// *guard += 1; // Access protected data
380/// } // Mutex automatically unlocked here
381/// ```
382pub struct MutexGuard<'a, T: ?Sized + 'a> {
383 mutex: &'a Mutex<T>,
384 _phantom: PhantomData<&'a mut T>,
385}
386
387impl<'a, T: ?Sized> Deref for MutexGuard<'a, T> {
388 type Target = T;
389
390 fn deref(&self) -> &T {
391 unsafe { &*self.mutex.data.get() }
392 }
393}
394
395impl<'a, T: ?Sized> DerefMut for MutexGuard<'a, T> {
396 fn deref_mut(&mut self) -> &mut T {
397 unsafe { &mut *self.mutex.data.get() }
398 }
399}
400
401impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> {
402 fn drop(&mut self) {
403 self.mutex.inner.unlock();
404 }
405}
406
407impl<'a, T: ?Sized> MutexGuardFn<'a, T> for MutexGuard<'a, T> {
408 /// Updates the protected value with a new value.
409 ///
410 /// # Note
411 ///
412 /// This requires `T` to implement `Clone` to copy the value.
413 /// Use the dereference operator directly for types that implement `Copy`.
414 ///
415 /// # Examples
416 ///
417 /// ```ignore
418 /// let mut guard = mutex.lock().unwrap();
419 /// let new_value = 42;
420 /// guard.update(&new_value);
421 /// ```
422 fn update(&mut self, t: &T)
423 where
424 T: Clone
425 {
426 // Dereference twice: first to get &mut T from MutexGuard,
427 // then assign the cloned value
428 **self = t.clone();
429 }
430}
431
432/// RAII guard returned by `Mutex::lock_from_isr()`.
433///
434/// Similar to `MutexGuard` but specifically for ISR context.
435/// Automatically unlocks the mutex when dropped using ISR-safe unlock.
436///
437/// # Examples
438///
439/// ```ignore
440/// // In ISR context:
441/// if let Ok(mut guard) = mutex.lock_from_isr() {
442/// *guard = new_value;
443/// } // Automatically unlocked with ISR-safe method
444/// ```
445pub struct MutexGuardFromIsr<'a, T: ?Sized + 'a> {
446 mutex: &'a Mutex<T>,
447 _phantom: PhantomData<&'a mut T>,
448}
449
450impl<'a, T: ?Sized> Deref for MutexGuardFromIsr<'a, T> {
451 type Target = T;
452
453 fn deref(&self) -> &T {
454 unsafe { &*self.mutex.data.get() }
455 }
456}
457
458impl<'a, T: ?Sized> DerefMut for MutexGuardFromIsr<'a, T> {
459 fn deref_mut(&mut self) -> &mut T {
460 unsafe { &mut *self.mutex.data.get() }
461 }
462}
463
464impl<'a, T: ?Sized> Drop for MutexGuardFromIsr<'a, T> {
465 fn drop(&mut self) {
466 self.mutex.inner.unlock_from_isr();
467 }
468}
469
470impl<'a, T: ?Sized> MutexGuardFn<'a, T> for MutexGuardFromIsr<'a, T> {
471 /// Updates the protected value from ISR context.
472 ///
473 /// # Note
474 ///
475 /// This requires `T` to implement `Clone` to copy the value.
476 ///
477 /// # Examples
478 ///
479 /// ```ignore
480 /// // In ISR context:
481 /// if let Ok(mut guard) = mutex.lock_from_isr() {
482 /// guard.update(&new_value);
483 /// }
484 /// ```
485 fn update(&mut self, t: &T)
486 where
487 T: Clone
488 {
489 **self = t.clone();
490 }
491}
492
493//// StaticMutex ////
494
495/// A statically-allocated mutex for protecting shared data.
496///
497/// Similar to `Mutex<T>` but designed to be allocated at compile time
498/// as a static variable. Requires explicit initialization before use.
499///
500/// # Examples
501///
502/// ```ignore
503/// use osal_rs::os::StaticMutex;
504///
505/// static mut COUNTER_MUTEX: Option<StaticMutex<u32>> = None;
506///
507/// // During initialization:
508/// unsafe {
509/// COUNTER_MUTEX = Some(StaticMutex::new(0).unwrap());
510/// }
511/// ```
512pub struct StaticMutex<T: ?Sized> {
513 inner: RawMutex,
514 data: UnsafeCell<T>,
515}
516
517impl<T> StaticMutex<T> {
518 /// Creates a new static mutex protecting the given data.
519 ///
520 /// # Parameters
521 ///
522 /// * `data` - The initial value to protect
523 ///
524 /// # Returns
525 ///
526 /// * `Ok(StaticMutex)` - Successfully created
527 /// * `Err(Error::OutOfMemory)` - Failed to allocate mutex resources
528 ///
529 /// # Examples
530 ///
531 /// ```ignore
532 /// let mutex = StaticMutex::new(42).unwrap();
533 /// ```
534 pub fn new(data: T) -> Result<Self> {
535 Ok(Self {
536 inner: RawMutex::new()?,
537 data: UnsafeCell::new(data),
538 })
539 }
540}
541
542
543impl<T> StaticMutexFn<T> for StaticMutex<T> {
544 type Guard<'a> = StaticMutexGuard<'a, T> where Self: 'a, T: 'a;
545 type GuardFromIsr<'a> = StaticMutexGuardFromIsr<'a, T> where Self: 'a, T: 'a;
546
547 /// Acquires the lock and returns a guard.
548 ///
549 /// Blocks until the lock is available.
550 fn lock(&self) -> Result<StaticMutexGuard<'_, T>> {
551 match self.inner.lock() {
552 OsalRsBool::True => Ok(StaticMutexGuard {
553 mutex: self,
554 _phantom: core::marker::PhantomData,
555 }),
556 OsalRsBool::False => Err(Error::MutexLockFailed),
557 }
558 }
559
560 /// Acquires the lock from ISR context and returns an ISR guard.
561 ///
562 /// Non-blocking version for interrupt service routines.
563 fn lock_from_isr(&self) -> Result<StaticMutexGuardFromIsr<'_, T>> {
564 match self.inner.lock_from_isr() {
565 OsalRsBool::True => Ok(StaticMutexGuardFromIsr {
566 mutex: self,
567 _phantom: core::marker::PhantomData,
568 }),
569 OsalRsBool::False => Err(Error::MutexLockFailed),
570 }
571 }
572
573 /// Returns a mutable reference to the underlying data.
574 ///
575 /// Safe because it requires exclusive mutable access to the mutex.
576 fn get_mut(&mut self) -> &mut T {
577 unsafe { &mut *self.data.get() }
578 }
579}
580
581impl<T> StaticMutex<T> {
582 /// Initializes the mutex.
583 ///
584 /// Note: The mutex is already initialized in the constructor.
585 /// This method is provided for API compatibility.
586 ///
587 /// # Returns
588 ///
589 /// Always returns `Ok(())`
590 pub fn init(&mut self) -> Result<()> {
591 // Mutex is already initialized in the constructor
592 // This method exists for API compatibility
593 Ok(())
594 }
595}
596
597/// RAII guard for `StaticMutex` returned by `lock()`.
598///
599/// Automatically releases the mutex when dropped. Provides access to
600/// the protected data through `Deref` and `DerefMut`.
601pub struct StaticMutexGuard<'a, T: ?Sized + 'a> {
602 mutex: &'a StaticMutex<T>,
603 _phantom: core::marker::PhantomData<&'a mut T>,
604}
605
606impl<'a, T: ?Sized> Deref for StaticMutexGuard<'a, T> {
607 type Target = T;
608
609 fn deref(&self) -> &T {
610 unsafe { &*self.mutex.data.get() }
611 }
612}
613
614impl<'a, T: ?Sized> DerefMut for StaticMutexGuard<'a, T> {
615 fn deref_mut(&mut self) -> &mut T {
616 unsafe { &mut *self.mutex.data.get() }
617 }
618}
619
620impl<'a, T: ?Sized> Drop for StaticMutexGuard<'a, T> {
621 fn drop(&mut self) {
622 self.mutex.inner.unlock();
623 }
624}
625
626/// RAII guard for `StaticMutex` returned by `lock_from_isr()`.
627///
628/// Similar to `StaticMutexGuard` but for ISR context. Automatically
629/// releases the mutex using ISR-safe unlock when dropped.
630pub struct StaticMutexGuardFromIsr<'a, T: ?Sized + 'a> {
631 mutex: &'a StaticMutex<T>,
632 _phantom: core::marker::PhantomData<&'a mut T>,
633}
634
635impl<'a, T: ?Sized> Deref for StaticMutexGuardFromIsr<'a, T> {
636 type Target = T;
637
638 fn deref(&self) -> &T {
639 unsafe { &*self.mutex.data.get() }
640 }
641}
642
643impl<'a, T: ?Sized> DerefMut for StaticMutexGuardFromIsr<'a, T> {
644 fn deref_mut(&mut self) -> &mut T {
645 unsafe { &mut *self.mutex.data.get() }
646 }
647}
648
649impl<'a, T: ?Sized> Drop for StaticMutexGuardFromIsr<'a, T> {
650 fn drop(&mut self) {
651 self.mutex.inner.unlock_from_isr();
652 }
653}