Skip to main content

osal_rs/traits/
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 trait definitions.
22//!
23//! This module provides traits for mutual exclusion (mutex) synchronization
24//! primitives, enabling safe shared access to data across multiple tasks.
25//!
26//! # Overview
27//!
28//! Mutexes prevent race conditions by ensuring only one task can access
29//! protected data at a time. This module provides both low-level raw mutex
30//! operations and high-level RAII-style interfaces.
31//!
32//! # Concepts
33//!
34//! - **RAII Guards**: Locks are automatically released when guard goes out of scope
35//! - **Priority Inheritance**: Some implementations support priority inheritance to prevent priority inversion
36//! - **ISR Safety**: Special methods for use in interrupt service routines
37//!
38//! # Deadlock Prevention
39//!
40//! - Always acquire mutexes in the same order
41//! - Don't hold locks longer than necessary
42//! - Avoid calling blocking operations while holding a lock
43//!
44//! # Examples
45//!
46//! ```
47//! use osal_rs::os::{Mutex, MutexFn};
48//!
49//! let mutex = Mutex::new(0);
50//!
51//! // Lock automatically released when guard goes out of scope
52//! {
53//!     let mut guard = mutex.lock().unwrap();
54//!     *guard += 1;
55//! } // Lock released here
56//! ```
57
58use crate::utils::{OsalRsBool, Result};
59
60/// Low-level raw mutex operations.
61///
62/// This trait defines the basic mutex primitives that interface directly
63/// with the underlying RTOS mutex implementation.
64///
65/// # Implementation Notes
66///
67/// Implementations should support priority inheritance where available to
68/// prevent priority inversion problems in real-time systems.
69///
70/// # Safety
71///
72/// - `lock()` must only be called from task context (not ISR)
73/// - `lock_from_isr()` must only be called from ISR context
74/// - `unlock()` must be called by the same task that acquired the lock
75/// - Deadlocks can occur if locks are not acquired in consistent order
76///
77/// # Examples
78///
79/// ```ignore
80/// use osal_rs::traits::RawMutex;
81///
82/// // Acquire and release lock
83/// if raw_mutex.lock() {
84///     // Critical section
85///     raw_mutex.unlock();
86/// }
87/// ```
88pub trait RawMutex
89where
90    Self: Sized,
91{
92    /// Returns `true` if the underlying OS handle is null, i.e. the mutex
93    /// has not been created yet or has already been deleted.
94    fn is_null(&self) -> bool;
95
96    /// Locks the mutex (blocking).
97    ///
98    /// Blocks the calling task until the mutex becomes available.
99    /// Must only be called from task context, not from ISR.
100    ///
101    /// # Returns
102    ///
103    /// * `True` - Lock was successfully acquired
104    /// * `False` - Lock acquisition failed (should be rare)
105    ///
106    /// # Examples
107    ///
108    /// ```ignore
109    /// if raw_mutex.lock() {
110    ///     // Protected code here
111    ///     raw_mutex.unlock();
112    /// }
113    /// ```
114    fn lock(&self) -> OsalRsBool;
115
116    /// Locks the mutex from ISR context (non-blocking).
117    ///
118    /// Attempts to acquire the lock without blocking. Must only be
119    /// called from interrupt service routine context.
120    ///
121    /// # Returns
122    ///
123    /// * `True` - Lock was successfully acquired
124    /// * `False` - Lock is currently held by another task
125    ///
126    /// # Note
127    ///
128    /// This is a try-lock operation that returns immediately.
129    ///
130    /// # Examples
131    ///
132    /// ```ignore
133    /// // In ISR handler
134    /// if raw_mutex.lock_from_isr() {
135    ///     // Quick critical operation
136    ///     raw_mutex.unlock_from_isr();
137    /// }
138    /// ```
139    fn lock_from_isr(&self) -> OsalRsBool;
140
141    /// Unlocks the mutex.
142    ///
143    /// Releases the mutex that was previously acquired by `lock()`.
144    /// Must be called by the same task that acquired the lock.
145    ///
146    /// # Returns
147    ///
148    /// * `True` - Unlock succeeded
149    /// * `False` - Unlock failed (mutex not owned by caller)
150    ///
151    /// # Safety
152    ///
153    /// Calling unlock on a mutex not owned by the current task
154    /// may cause undefined behavior.
155    fn unlock(&self) -> OsalRsBool;
156
157    /// Unlocks the mutex from ISR context.
158    ///
159    /// Releases the mutex that was previously acquired by `lock_from_isr()`.
160    /// Must only be called from interrupt context.
161    ///
162    /// # Returns
163    ///
164    /// * `True` - Unlock succeeded
165    /// * `False` - Unlock failed
166    fn unlock_from_isr(&self) -> OsalRsBool;
167
168    /// Deletes the mutex and frees its resources.
169    ///
170    /// # Safety
171    ///
172    /// The mutex must not be locked by any task when this is called.
173    /// Ensure no tasks are waiting for this mutex before deletion.
174    ///
175    /// # Examples
176    ///
177    /// ```ignore
178    /// let mut raw_mutex = create_raw_mutex();
179    /// // Use mutex...
180    /// raw_mutex.delete();
181    /// ```
182    fn delete(&mut self);
183}
184
185/// Marker trait for mutex guard types.
186///
187/// Implemented by types that represent active mutex locks. Guards
188/// automatically release the mutex when dropped (RAII pattern).
189///
190/// # Lifetime
191///
192/// The `'a` lifetime ensures the guard cannot outlive the mutex it guards.
193///
194/// # Auto-Unlock
195///
196/// The mutex is automatically unlocked when the guard goes out of scope,
197/// ensuring locks are always properly released even if a panic occurs.
198pub trait MutexGuard<'a, T: ?Sized + 'a> {
199    /// Updates the value protected by the mutex guard.
200    ///
201    /// Clones the provided value and replaces the current value
202    /// protected by the mutex.
203    ///
204    /// # Parameters
205    ///
206    /// * `t` - Reference to the new value to assign
207    ///
208    /// # Type Requirements
209    ///
210    /// The type `T` must implement `Clone`.
211    ///
212    /// # Examples
213    ///
214    /// ```ignore
215    /// use osal_rs::os::Mutex;
216    /// use osal_rs::traits::MutexGuard;
217    /// 
218    /// let mutex = Mutex::new(0);
219    /// let mut guard = mutex.lock().unwrap();
220    /// 
221    /// // Update with new value
222    /// guard.update(&42);
223    /// assert_eq!(*guard, 42);
224    /// 
225    /// // Lock is automatically released when guard drops
226    /// ```
227    fn update(&mut self, t: &T)
228    where
229        T: Clone;
230
231}
232
233/// High-level mutex trait with type-safe data protection.
234///
235/// This trait provides RAII-style mutex operations with automatic lock
236/// management through guard types. The mutex owns the data it protects,
237/// ensuring data can only be accessed through a locked guard.
238///
239/// # Type Safety
240///
241/// The data type `T` is protected at compile time - you cannot access
242/// the data without holding the lock.
243///
244/// # Examples
245///
246/// ```ignore
247/// use osal_rs::os::Mutex;
248///
249/// let counter = Mutex::new(0);
250///
251/// // Task 1
252/// {
253///     let mut guard = counter.lock().unwrap();
254///     *guard += 1;
255/// } // Lock released here
256///
257/// // Task 2
258/// {
259///     let guard = counter.lock().unwrap();
260///     println!("Counter: {}", *guard);
261/// }
262/// ```
263pub trait Mutex<T: ?Sized> {
264    /// The guard type for normal mutex locks
265    type Guard<'a>: MutexGuard<'a, T> where Self: 'a, T: 'a;
266    /// The guard type for ISR-context mutex locks
267    type GuardFromIsr<'a>: MutexGuard<'a, T> where Self: 'a, T: 'a;
268
269    /// Acquires the mutex, blocking the current task until it is able to do so.
270    ///
271    /// This method will block until the lock can be acquired. When the lock
272    /// is acquired, a guard is returned that provides access to the protected
273    /// data and automatically releases the lock when dropped.
274    ///
275    /// # Returns
276    ///
277    /// * `Ok(Guard)` - Lock acquired successfully
278    /// * `Err(Error)` - Lock acquisition failed (rare)
279    ///
280    /// # Panics
281    ///
282    /// May panic if called from ISR context. Use `lock_from_isr()` instead.
283    ///
284    /// # Examples
285    ///
286    /// ```ignore
287    /// let mutex = Mutex::new(vec![1, 2, 3]);
288    ///
289    /// let mut guard = mutex.lock().unwrap();
290    /// guard.push(4);
291    /// // Lock automatically released when guard goes out of scope
292    /// ```
293    fn lock(&self) -> Result<Self::Guard<'_>>;
294    
295    /// Acquires the mutex from ISR context.
296    ///
297    /// This is a non-blocking attempt to acquire the mutex, suitable for
298    /// use in interrupt service routines. Returns immediately whether or
299    /// not the lock was acquired.
300    ///
301    /// # Returns
302    ///
303    /// * `Ok(GuardFromIsr)` - Lock acquired successfully
304    /// * `Err(Error)` - Lock is currently held, try again later
305    ///
306    /// # Examples
307    ///
308    /// ```ignore
309    /// // In interrupt handler
310    /// match mutex.lock_from_isr() {
311    ///     Ok(mut guard) => {
312    ///         *guard += 1;
313    ///         // Lock released when guard drops
314    ///     },
315    ///     Err(_) => {
316    ///         // Lock unavailable, skip or retry later
317    ///     }
318    /// }
319    /// ```
320    fn lock_from_isr(&self) -> Result<Self::GuardFromIsr<'_>>;
321
322    /// Attempts to consume this mutex, returning the underlying data.
323    ///
324    /// This method consumes the mutex and returns the protected data.
325    /// Since the mutex is consumed, no locking is required.
326    ///
327    /// # Returns
328    ///
329    /// * `Ok(T)` - The data that was protected by the mutex
330    /// * `Err(Error)` - Failed to consume mutex (e.g., still locked)
331    ///
332    /// # Examples
333    ///
334    /// ```ignore
335    /// let mutex = Mutex::new(vec![1, 2, 3]);
336    ///
337    /// let data = mutex.into_inner().unwrap();
338    /// assert_eq!(data, vec![1, 2, 3]);
339    /// ```
340    fn into_inner(self) -> Result<T> 
341    where 
342        Self: Sized, 
343        T: Sized;
344
345    /// Returns a mutable reference to the underlying data.
346    ///
347    /// This method does not require locking since it takes a mutable
348    /// reference to the mutex itself, which guarantees exclusive access
349    /// at compile time.
350    ///
351    /// # Returns
352    ///
353    /// A mutable reference to the protected data.
354    ///
355    /// # Examples
356    ///
357    /// ```ignore
358    /// let mut mutex = Mutex::new(0);
359    ///
360    /// // No lock needed - we have exclusive access
361    /// *mutex.get_mut() = 42;
362    /// ```
363    fn get_mut(&mut self) -> &mut T;
364}
365
366/// RAII guard for a `'static` [`RawMutex`] implementation.
367///
368/// Locks the mutex on construction and unlocks it on drop, so callers only
369/// need to hold on to the guard for the duration of the critical section.
370/// This is intended for the common pattern of a module-level
371/// `static mut Option<M>` mutex guarding a module-level `static mut` resource,
372/// e.g. obtained via `access_static_option!`.
373///
374/// # Examples
375///
376/// ```ignore
377/// use osal_rs::traits::RawMutexGuard;
378/// use osal_rs::{access_static_option, os::RawMutex};
379///
380/// static mut MUTEX: Option<RawMutex> = None;
381///
382/// fn critical_section() {
383///     let _lock = RawMutexGuard::acquire(access_static_option!(MUTEX));
384///     // protected code here, lock released when `_lock` drops
385/// }
386/// ```
387pub struct RawMutexGuard<M: RawMutex + 'static>(&'static M, bool);
388
389impl<M: RawMutex + 'static> RawMutexGuard<M> {
390    /// Locks `mutex` and returns a guard that unlocks it on drop.
391    pub fn acquire(mutex: &'static M) -> Self {
392        mutex.lock();
393        Self(mutex, true)
394    }
395
396    /// Locks `mutex` via [`RawMutex::lock_from_isr`] instead of
397    /// [`RawMutex::lock`], and returns a guard that unlocks the same way on
398    /// drop. Use this instead of [`RawMutexGuard::acquire`] when the calling
399    /// context is (or might be) an interrupt handler.
400    pub fn acquire_from_isr(mutex: &'static M) -> Self {
401        mutex.lock_from_isr();
402        Self(mutex, false)
403    }
404}
405
406impl<M: RawMutex + 'static> Drop for RawMutexGuard<M> {
407    fn drop(&mut self) {
408        if self.1 {
409            self.0.unlock();
410        } else {
411            self.0.unlock_from_isr();
412        }
413    }
414}