Skip to main content

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