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