Skip to main content

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::traits::SystemFn;
36use crate::traits::{MutexGuardFn, RawMutexFn, MutexFn, ToTick};
37use crate::utils::{Result, Error, OsalRsBool, MAX_DELAY};
38use crate::{vSemaphoreDelete, xSemaphoreCreateRecursiveMutex, xSemaphoreGiveFromISR, xSemaphoreGiveRecursive, xSemaphoreTakeFromISR, xSemaphoreTakeRecursive};
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    
76    fn lock(&self) -> OsalRsBool {
77        let res = xSemaphoreTakeRecursive!(self.0, MAX_DELAY.to_ticks());
78        if res == pdTRUE {
79            OsalRsBool::True
80        } else {
81            OsalRsBool::False
82        }
83    }
84
85    fn lock_from_isr(&self) -> OsalRsBool {
86        let mut higher_priority_task_woken = pdFALSE;
87        let res = xSemaphoreTakeFromISR!(self.0, &mut higher_priority_task_woken);
88        if res == pdTRUE {
89
90            System::yield_from_isr(higher_priority_task_woken);
91
92            OsalRsBool::True
93        } else {
94            OsalRsBool::False
95        }
96    }
97
98    fn unlock(&self) -> OsalRsBool {
99        let res = xSemaphoreGiveRecursive!(self.0);
100        if res == pdTRUE {
101            OsalRsBool::True
102        } else {
103            OsalRsBool::False
104        }
105    }
106
107
108    fn unlock_from_isr(&self) -> OsalRsBool {
109        let mut higher_priority_task_woken = pdFALSE;
110        let res = xSemaphoreGiveFromISR!(self.0, &mut higher_priority_task_woken);
111        if res == pdTRUE {
112            
113            System::yield_from_isr(higher_priority_task_woken);
114            
115            OsalRsBool::True
116        } else {
117            OsalRsBool::False
118        }
119    }
120
121    fn delete(&mut self) {
122        vSemaphoreDelete!(self.0);
123        self.0 = core::ptr::null();
124    }
125}
126
127impl Drop for RawMutex {
128    fn drop(&mut self) {
129        if self.0.is_null() {
130            return;
131        }
132        self.delete();
133    }
134}
135
136impl Deref for RawMutex {
137    type Target = MutexHandle;
138
139    fn deref(&self) -> &MutexHandle {
140        &self.0
141    }
142}
143
144
145impl Debug for RawMutex {
146    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
147        f.debug_struct("RawMutex")
148            .field("handle", &self.0)
149            .finish()
150    }
151}
152
153impl Display for RawMutex {
154    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
155        write!(f, "RawMutex {{ handle: {:?} }}", self.0)
156    }
157}
158
159//// Mutex ////
160
161/// A mutual exclusion primitive useful for protecting shared data.
162///
163/// This mutex will block threads waiting for the lock to become available.
164/// The mutex is implemented using FreeRTOS recursive mutexes, supporting
165/// priority inheritance to prevent priority inversion.
166///
167/// # Type Parameters
168///
169/// * `T` - The type of data protected by the mutex
170///
171/// # Examples
172///
173/// ## Basic usage
174///
175/// ```ignore
176/// use osal_rs::os::Mutex;
177/// 
178/// let mutex = Mutex::new(0);
179/// 
180/// // Acquire the lock and modify the data
181/// {
182///     let mut guard = mutex.lock().unwrap();
183///     *guard += 1;
184/// }  // Lock is automatically released here
185/// ```
186///
187/// ## Sharing between threads
188///
189/// ```ignore
190/// use osal_rs::os::{Mutex, Thread};
191/// use alloc::sync::Arc;
192/// 
193/// let counter = Arc::new(Mutex::new(0));
194/// let counter_clone = counter.clone();
195/// 
196/// let thread = Thread::new("worker", 2048, 5, move || {
197///     let mut guard = counter_clone.lock().unwrap();
198///     *guard += 1;
199/// }).unwrap();
200/// 
201/// thread.start().unwrap();
202/// ```
203///
204/// ## Using from ISR context
205///
206/// ```ignore
207/// use osal_rs::os::Mutex;
208/// 
209/// let mutex = Mutex::new(0);
210/// 
211/// // In an interrupt handler:
212/// if let Ok(mut guard) = mutex.lock_from_isr() {
213///     *guard = 42;
214/// }
215/// ```
216pub struct Mutex<T: ?Sized> {
217    inner: RawMutex,
218    data: UnsafeCell<T>
219}
220
221
222unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
223unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
224
225impl<T: ?Sized>  Mutex<T> {
226        
227    /// Creates a new mutex wrapping the supplied data.
228    ///
229    /// # Examples
230    ///
231    /// ```ignore
232    /// use osal_rs::os::{Mutex, MutexFn};
233    /// 
234    /// let mutex = Mutex::new(0);
235    pub fn new(data: T) -> Self
236    where 
237        T: Sized
238    {
239        Self {
240            inner: RawMutex::new().unwrap(),
241            data: UnsafeCell::new(data),
242        }
243    }
244
245    fn get_mut(&mut self) -> &mut T {
246        unsafe { &mut *self.data.get() }
247    }
248}
249
250impl<T: ?Sized> MutexFn<T> for Mutex<T> {
251    type Guard<'a> = MutexGuard<'a, T> where Self: 'a, T: 'a;
252    type GuardFromIsr<'a> = MutexGuardFromIsr<'a, T> where Self: 'a, T: 'a;
253
254
255    fn lock(&self) -> Result<Self::Guard<'_>> {
256        match self.inner.lock() {
257            OsalRsBool::True => Ok(MutexGuard {
258                mutex: self,
259                _phantom: PhantomData,
260            }),
261            OsalRsBool::False => Err(Error::MutexLockFailed),
262        }
263    }
264
265    fn lock_from_isr(&self) -> Result<Self::GuardFromIsr<'_>> {
266        match self.inner.lock_from_isr() {
267            OsalRsBool::True => Ok(MutexGuardFromIsr {
268                mutex: self,
269                _phantom: PhantomData,
270            }),
271            OsalRsBool::False => Err(Error::MutexLockFailed),
272        }
273    }
274
275    /// Consumes the mutex and returns the inner data.
276    ///
277    /// This is safe because we have unique ownership of the mutex.
278    ///
279    /// # Examples
280    ///
281    /// ```ignore
282    /// use osal_rs::os::{Mutex, MutexFn};
283    /// 
284    /// let mutex = Mutex::new(5);
285    /// let value = mutex.into_inner().unwrap();
286    /// assert_eq!(value, 5);
287    /// ```
288    fn into_inner(self) -> Result<T> 
289    where 
290        Self: Sized, 
291        T: Sized 
292    {
293        Ok(self.data.into_inner())
294    }
295
296    /// Returns a mutable reference to the inner data.
297    ///
298    /// Since this takes `&mut self`, we know there are no other references
299    /// to the data, so we can safely return a mutable reference.
300    ///
301    /// # Examples
302    ///
303    /// ```ignore
304    /// use osal_rs::os::{Mutex, MutexFn};
305    /// 
306    /// let mut mutex = Mutex::new(0);
307    /// *mutex.get_mut() = 10;
308    /// assert_eq!(*mutex.get_mut(), 10);
309    /// ```
310    fn get_mut(&mut self) -> &mut T {
311        self.data.get_mut()
312    }
313}
314
315impl<T: ?Sized> Mutex<T> {
316    /// Acquires the mutex from ISR context, returning a specific ISR guard.
317    ///
318    /// This is an explicit version of `lock_from_isr` that returns the ISR-specific guard type.
319    ///
320    /// # Returns
321    ///
322    /// * `Ok(MutexGuardFromIsr)` - Lock acquired
323    /// * `Err(Error::MutexLockFailed)` - Failed to acquire lock
324    ///
325    /// # Examples
326    ///
327    /// ```ignore
328    /// // In ISR context:
329    /// if let Ok(guard) = mutex.lock_from_isr_explicit() {
330    ///     *guard = new_value;
331    /// }
332    /// ```
333    pub fn lock_from_isr_explicit(&self) -> Result<MutexGuardFromIsr<'_, T>> {
334        match self.inner.lock_from_isr() {
335            OsalRsBool::True => Ok(MutexGuardFromIsr {
336                mutex: self,
337                _phantom: PhantomData,
338            }),
339            OsalRsBool::False => Err(Error::MutexLockFailed),
340        }
341    }
342}
343
344impl<T> Mutex<T> {
345    /// Creates a new mutex wrapped in an Arc for easy sharing between threads.
346    ///
347    /// This is a convenience method equivalent to `Arc::new(Mutex::new(data))`.
348    ///
349    /// # Examples
350    ///
351    /// ```ignore
352    /// use osal_rs::os::Mutex;
353    /// use alloc::sync::Arc;
354    /// 
355    /// let shared_data = Mutex::new_arc(0u32);
356    /// let data_clone = Arc::clone(&shared_data);
357    /// 
358    /// // Use in thread...
359    /// let thread = Thread::new("worker", 2048, 5, move || {
360    ///     let mut guard = data_clone.lock().unwrap();
361    ///     *guard += 1;
362    /// });
363    /// ```
364    pub fn new_arc(data: T) -> Arc<Self> {
365        Arc::new(Self::new(data))
366    }
367}
368
369impl<T> Debug for Mutex<T> 
370where 
371    T: ?Sized {
372    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
373        f.debug_struct("Mutex")
374            .field("inner", &self.inner)
375            .finish()
376    }
377}
378
379impl<T> Display for Mutex<T> 
380where 
381    T: ?Sized {
382    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
383        write!(f, "Mutex {{ inner: {} }}", self.inner)
384    }   
385}
386
387//// MutexGuard ////
388
389/// RAII guard returned by `Mutex::lock()`.
390///
391/// When this guard goes out of scope, the mutex is automatically unlocked.
392/// Provides access to the protected data through `Deref` and `DerefMut`.
393///
394/// # Examples
395///
396/// ```ignore
397/// use osal_rs::os::{Mutex, MutexFn};
398/// 
399/// let mutex = Mutex::new(0);
400/// 
401/// {
402///     let mut guard = mutex.lock().unwrap();
403///     *guard += 1;  // Access protected data
404/// }  // Mutex automatically unlocked here
405/// ```
406pub struct MutexGuard<'a, T: ?Sized + 'a> {
407    mutex: &'a Mutex<T>,
408    _phantom: PhantomData<&'a mut T>,
409}
410
411impl<'a, T: ?Sized> Deref for MutexGuard<'a, T> {
412    type Target = T;
413
414    fn deref(&self) -> &T {
415        unsafe { &*self.mutex.data.get() }
416    }
417}
418
419impl<'a, T: ?Sized> DerefMut for MutexGuard<'a, T> {
420    fn deref_mut(&mut self) -> &mut T {
421        unsafe { &mut *self.mutex.data.get() }
422    }
423}
424
425impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> {
426    fn drop(&mut self) {
427        self.mutex.inner.unlock();
428    }
429}
430
431impl<'a, T: ?Sized> MutexGuardFn<'a, T> for MutexGuard<'a, T> {
432    /// Updates the protected value with a new value.
433    ///
434    /// # Note
435    ///
436    /// This requires `T` to implement `Clone` to copy the value.
437    /// Use the dereference operator directly for types that implement `Copy`.
438    ///
439    /// # Examples
440    ///
441    /// ```ignore
442    /// let mut guard = mutex.lock().unwrap();
443    /// let new_value = 42;
444    /// guard.update(&new_value);
445    /// ```
446    fn update(&mut self, t: &T) 
447    where
448        T: Clone
449    {
450        // Dereference twice: first to get &mut T from MutexGuard,
451        // then assign the cloned value
452        **self = t.clone();
453    }
454}
455
456/// RAII guard returned by `Mutex::lock_from_isr()`.
457///
458/// Similar to `MutexGuard` but specifically for ISR context.
459/// Automatically unlocks the mutex when dropped using ISR-safe unlock.
460///
461/// # Examples
462///
463/// ```ignore
464/// // In ISR context:
465/// if let Ok(mut guard) = mutex.lock_from_isr() {
466///     *guard = new_value;
467/// }  // Automatically unlocked with ISR-safe method
468/// ```
469pub struct MutexGuardFromIsr<'a, T: ?Sized + 'a> {
470    mutex: &'a Mutex<T>,
471    _phantom: PhantomData<&'a mut T>,
472}
473
474impl<'a, T: ?Sized> Deref for MutexGuardFromIsr<'a, T> {
475    type Target = T;
476
477    fn deref(&self) -> &T {
478        unsafe { &*self.mutex.data.get() }
479    }
480}
481
482impl<'a, T: ?Sized> DerefMut for MutexGuardFromIsr<'a, T> {
483    fn deref_mut(&mut self) -> &mut T {
484        unsafe { &mut *self.mutex.data.get() }
485    }
486}
487
488impl<'a, T: ?Sized> Drop for MutexGuardFromIsr<'a, T> {
489    fn drop(&mut self) {
490        self.mutex.inner.unlock_from_isr();
491    }
492}
493
494impl<'a, T: ?Sized> MutexGuardFn<'a, T> for MutexGuardFromIsr<'a, T> {
495    /// Updates the protected value from ISR context.
496    ///
497    /// # Note
498    ///
499    /// This requires `T` to implement `Clone` to copy the value.
500    ///
501    /// # Examples
502    ///
503    /// ```ignore
504    /// // In ISR context:
505    /// if let Ok(mut guard) = mutex.lock_from_isr() {
506    ///     guard.update(&new_value);
507    /// }
508    /// ```
509    fn update(&mut self, t: &T) 
510    where
511        T: Clone
512    {
513        **self = t.clone();
514    }
515}