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}