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}