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