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}