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