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}