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}