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/// ```
80/// use osal_rs::os::*;
81/// use osal_rs::utils::OsalRsBool;
82///
83/// let raw_mutex = RawMutex::new().unwrap();
84///
85/// // Acquire and release lock
86/// if raw_mutex.lock() == OsalRsBool::True {
87///     // Critical section
88///     assert_eq!(raw_mutex.unlock(), OsalRsBool::True);
89/// }
90/// ```
91pub trait RawMutex
92where
93    Self: Sized,
94{
95    /// Returns `true` if the underlying OS handle is null, i.e. the mutex
96    /// has not been created yet or has already been deleted.
97    fn is_null(&self) -> bool;
98
99    /// Locks the mutex (blocking).
100    ///
101    /// Blocks the calling task until the mutex becomes available.
102    /// Must only be called from task context, not from ISR.
103    ///
104    /// # Returns
105    ///
106    /// * `True` - Lock was successfully acquired
107    /// * `False` - Lock acquisition failed (should be rare)
108    ///
109    /// # Examples
110    ///
111    /// ```
112    /// use osal_rs::os::*;
113    /// use osal_rs::utils::OsalRsBool;
114    ///
115    /// let raw_mutex = RawMutex::new().unwrap();
116    ///
117    /// if raw_mutex.lock() == OsalRsBool::True {
118    ///     // Protected code here
119    ///     raw_mutex.unlock();
120    /// }
121    /// ```
122    fn lock(&self) -> OsalRsBool;
123
124    /// Locks the mutex from ISR context (non-blocking).
125    ///
126    /// Attempts to acquire the lock without blocking. Must only be
127    /// called from interrupt service routine context.
128    ///
129    /// # Returns
130    ///
131    /// * `True` - Lock was successfully acquired
132    /// * `False` - Lock is currently held by another task
133    ///
134    /// # Note
135    ///
136    /// This is a try-lock operation that returns immediately.
137    ///
138    /// # Examples
139    ///
140    /// ```
141    /// use osal_rs::os::*;
142    /// use osal_rs::utils::OsalRsBool;
143    ///
144    /// let raw_mutex = RawMutex::new().unwrap();
145    ///
146    /// // In ISR handler
147    /// if raw_mutex.lock_from_isr() == OsalRsBool::True {
148    ///     // Quick critical operation
149    ///     raw_mutex.unlock_from_isr();
150    /// }
151    /// ```
152    fn lock_from_isr(&self) -> OsalRsBool;
153
154    /// Unlocks the mutex.
155    ///
156    /// Releases the mutex that was previously acquired by `lock()`.
157    /// Must be called by the same task that acquired the lock.
158    ///
159    /// # Returns
160    ///
161    /// * `True` - Unlock succeeded
162    /// * `False` - Unlock failed (mutex not owned by caller)
163    ///
164    /// # Safety
165    ///
166    /// Calling unlock on a mutex not owned by the current task
167    /// may cause undefined behavior.
168    fn unlock(&self) -> OsalRsBool;
169
170    /// Unlocks the mutex from ISR context.
171    ///
172    /// Releases the mutex that was previously acquired by `lock_from_isr()`.
173    /// Must only be called from interrupt context.
174    ///
175    /// # Returns
176    ///
177    /// * `True` - Unlock succeeded
178    /// * `False` - Unlock failed
179    fn unlock_from_isr(&self) -> OsalRsBool;
180
181    /// Deletes the mutex and frees its resources.
182    ///
183    /// # Safety
184    ///
185    /// The mutex must not be locked by any task when this is called.
186    /// Ensure no tasks are waiting for this mutex before deletion.
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use osal_rs::os::*;
192    ///
193    /// let mut raw_mutex = RawMutex::new().unwrap();
194    ///
195    /// // Use mutex...
196    /// raw_mutex.lock();
197    /// raw_mutex.unlock();
198    ///
199    /// raw_mutex.delete();
200    /// assert!(raw_mutex.is_null());
201    /// ```
202    fn delete(&mut self);
203}
204
205/// Marker trait for mutex guard types.
206///
207/// Implemented by types that represent active mutex locks. Guards
208/// automatically release the mutex when dropped (RAII pattern).
209///
210/// # Lifetime
211///
212/// The `'a` lifetime ensures the guard cannot outlive the mutex it guards.
213///
214/// # Auto-Unlock
215///
216/// The mutex is automatically unlocked when the guard goes out of scope,
217/// ensuring locks are always properly released even if a panic occurs.
218pub trait MutexGuard<'a, T: ?Sized + 'a> {
219    /// Updates the value protected by the mutex guard.
220    ///
221    /// Clones the provided value and replaces the current value
222    /// protected by the mutex.
223    ///
224    /// # Parameters
225    ///
226    /// * `t` - Reference to the new value to assign
227    ///
228    /// # Type Requirements
229    ///
230    /// The type `T` must implement `Clone`.
231    ///
232    /// # Examples
233    ///
234    /// ```
235    /// use osal_rs::os::*;
236    ///
237    /// let mutex = Mutex::new(0);
238    ///
239    /// {
240    ///     let mut guard = mutex.lock().unwrap();
241    ///
242    ///     // Update with new value
243    ///     guard.update(&42);
244    ///     assert_eq!(*guard, 42);
245    /// } // Lock is automatically released when guard drops
246    ///
247    /// assert_eq!(*mutex.lock().unwrap(), 42);
248    /// ```
249    fn update(&mut self, t: &T)
250    where
251        T: Clone;
252
253}
254
255/// High-level mutex trait with type-safe data protection.
256///
257/// This trait provides RAII-style mutex operations with automatic lock
258/// management through guard types. The mutex owns the data it protects,
259/// ensuring data can only be accessed through a locked guard.
260///
261/// # Type Safety
262///
263/// The data type `T` is protected at compile time - you cannot access
264/// the data without holding the lock.
265///
266/// # Examples
267///
268/// ```
269/// use osal_rs::os::*;
270/// use std::sync::Arc;
271///
272/// let counter = Arc::new(Mutex::new(0));
273/// let shared = counter.clone();
274///
275/// // Task 1
276/// let mut thread = Thread::new("incrementer", 1024, 1);
277/// let worker = thread.spawn_simple(move || {
278///     let mut guard = shared.lock().unwrap();
279///     *guard += 1;
280///     Ok(Arc::new(()))
281/// }).unwrap();
282///
283/// worker.delete(); // waits for task 1 to finish
284///
285/// // Task 2
286/// {
287///     let guard = counter.lock().unwrap();
288///     assert_eq!(*guard, 1);
289/// } // Lock released here
290/// ```
291pub trait Mutex<T: ?Sized> {
292    /// The guard type for normal mutex locks
293    type Guard<'a>: MutexGuard<'a, T> where Self: 'a, T: 'a;
294    /// The guard type for ISR-context mutex locks
295    type GuardFromIsr<'a>: MutexGuard<'a, T> where Self: 'a, T: 'a;
296
297    /// Acquires the mutex, blocking the current task until it is able to do so.
298    ///
299    /// This method will block until the lock can be acquired. When the lock
300    /// is acquired, a guard is returned that provides access to the protected
301    /// data and automatically releases the lock when dropped.
302    ///
303    /// # Returns
304    ///
305    /// * `Ok(Guard)` - Lock acquired successfully
306    /// * `Err(Error)` - Lock acquisition failed (rare)
307    ///
308    /// # Panics
309    ///
310    /// May panic if called from ISR context. Use `lock_from_isr()` instead.
311    ///
312    /// # Examples
313    ///
314    /// ```
315    /// use osal_rs::os::*;
316    ///
317    /// let mutex = Mutex::new(vec![1, 2, 3]);
318    ///
319    /// {
320    ///     let mut guard = mutex.lock().unwrap();
321    ///     guard.push(4);
322    /// } // Lock automatically released when guard goes out of scope
323    ///
324    /// assert_eq!(*mutex.lock().unwrap(), vec![1, 2, 3, 4]);
325    /// ```
326    fn lock(&self) -> Result<Self::Guard<'_>>;
327    
328    /// Acquires the mutex from ISR context.
329    ///
330    /// This is a non-blocking attempt to acquire the mutex, suitable for
331    /// use in interrupt service routines. Returns immediately whether or
332    /// not the lock was acquired.
333    ///
334    /// # Returns
335    ///
336    /// * `Ok(GuardFromIsr)` - Lock acquired successfully
337    /// * `Err(Error)` - Lock is currently held, try again later
338    ///
339    /// # Examples
340    ///
341    /// ```
342    /// use osal_rs::os::*;
343    ///
344    /// let mutex = Mutex::new(0);
345    ///
346    /// // In interrupt handler
347    /// match mutex.lock_from_isr() {
348    ///     Ok(mut guard) => {
349    ///         *guard += 1;
350    ///         // Lock released when guard drops
351    ///     },
352    ///     Err(_) => {
353    ///         // Lock unavailable, skip or retry later
354    ///     }
355    /// }
356    ///
357    /// assert_eq!(*mutex.lock().unwrap(), 1);
358    /// ```
359    fn lock_from_isr(&self) -> Result<Self::GuardFromIsr<'_>>;
360
361    /// Attempts to consume this mutex, returning the underlying data.
362    ///
363    /// This method consumes the mutex and returns the protected data.
364    /// Since the mutex is consumed, no locking is required.
365    ///
366    /// # Returns
367    ///
368    /// * `Ok(T)` - The data that was protected by the mutex
369    /// * `Err(Error)` - Failed to consume mutex (e.g., still locked)
370    ///
371    /// # Examples
372    ///
373    /// ```
374    /// use osal_rs::os::*;
375    ///
376    /// let mutex = Mutex::new(vec![1, 2, 3]);
377    ///
378    /// let data = mutex.into_inner().unwrap();
379    /// assert_eq!(data, vec![1, 2, 3]);
380    /// ```
381    fn into_inner(self) -> Result<T> 
382    where 
383        Self: Sized, 
384        T: Sized;
385
386    /// Returns a mutable reference to the underlying data.
387    ///
388    /// This method does not require locking since it takes a mutable
389    /// reference to the mutex itself, which guarantees exclusive access
390    /// at compile time.
391    ///
392    /// # Returns
393    ///
394    /// A mutable reference to the protected data.
395    ///
396    /// # Examples
397    ///
398    /// ```
399    /// use osal_rs::os::*;
400    ///
401    /// let mut mutex = Mutex::new(0);
402    ///
403    /// // No lock needed - we have exclusive access
404    /// *mutex.get_mut() = 42;
405    ///
406    /// assert_eq!(*mutex.lock().unwrap(), 42);
407    /// ```
408    fn get_mut(&mut self) -> &mut T;
409}
410
411/// RAII guard for a `'static` [`RawMutex`] implementation.
412///
413/// Locks the mutex on construction and unlocks it on drop, so callers only
414/// need to hold on to the guard for the duration of the critical section.
415/// This is intended for the common pattern of a module-level
416/// `static mut Option<M>` mutex guarding a module-level `static mut` resource,
417/// e.g. obtained via `access_static_option!`.
418///
419/// # Examples
420///
421/// ```
422/// use osal_rs::access_static_option;
423/// use osal_rs::os::*;
424///
425/// static mut MUTEX: Option<RawMutex> = None;
426///
427/// fn critical_section() {
428///     let _lock = RawMutexGuard::acquire(access_static_option!(MUTEX));
429///     // protected code here, lock released when `_lock` drops
430/// }
431///
432/// // Initialization phase: create the mutex before anyone locks it.
433/// unsafe { MUTEX = Some(RawMutex::new().unwrap()) };
434///
435/// critical_section();
436/// critical_section(); // the previous lock was released on drop
437/// ```
438pub struct RawMutexGuard<M: RawMutex + 'static>(&'static M, bool);
439
440impl<M: RawMutex + 'static> RawMutexGuard<M> {
441    /// Locks `mutex` and returns a guard that unlocks it on drop.
442    pub fn acquire(mutex: &'static M) -> Self {
443        mutex.lock();
444        Self(mutex, true)
445    }
446
447    /// Locks `mutex` via [`RawMutex::lock_from_isr`] instead of
448    /// [`RawMutex::lock`], and returns a guard that unlocks the same way on
449    /// drop. Use this instead of [`RawMutexGuard::acquire`] when the calling
450    /// context is (or might be) an interrupt handler.
451    pub fn acquire_from_isr(mutex: &'static M) -> Self {
452        mutex.lock_from_isr();
453        Self(mutex, false)
454    }
455}
456
457impl<M: RawMutex + 'static> Drop for RawMutexGuard<M> {
458    fn drop(&mut self) {
459        if self.1 {
460            self.0.unlock();
461        } else {
462            self.0.unlock_from_isr();
463        }
464    }
465}