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}