zephyr/sync/spinmutex.rs
1//! Spinlock-based Mutexes
2//!
3//! The [`sync::Mutex`] type is quite handy in Rust for sharing data between multiple contexts.
4//! However, it is not possible to aquire a mutex from interrupt context.
5//!
6//! This module provides a [`SpinMutex`] which has some similarities to the above, but with some
7//! restrictions. In contrast, however, it is usable from interrupt context.
8//!
9//! It works by use a spinlock to protect the contents of the SpinMutex. This allows for use in
10//! user threads as well as from irq context, the spinlock even protecting the data on SMP machines.
11//!
12//! In contract to [`critical_section::Mutex`], this has an API much closer to [`sync::Mutex`] (and
13//! as such to [`std::sync::Mutex`]. In addition, it has slightly less overhead on Zephyr, and
14//! since the mutex isn't shared, might allow for slightly better use on SMP systems, when the other
15//! CPU(s) don't need access to the SyncMutex.
16//!
17//! Note that `SpinMutex` doesn't have anything comparable to `Condvar`. Generally, they can be
18//! used with a `Semaphore` to allow clients to be waken, but this usage is racey, and if not done
19//! carefully can result uses of the semaphore not waking.
20
21use core::{
22 cell::UnsafeCell,
23 convert::Infallible,
24 fmt,
25 marker::PhantomData,
26 ops::{Deref, DerefMut},
27};
28
29use crate::raw;
30
31/// Result from the lock call. We keep the result for consistency of the API, but these can never
32/// fail.
33pub type SpinLockResult<Guard> = core::result::Result<Guard, Infallible>;
34
35/// Result from the `try_lock` call. There is only a single type of failure, indicating that this
36/// would block.
37pub type SpinTryLockResult<Guard> = core::result::Result<Guard, SpinTryLockError>;
38
39/// The single error type that can be returned from `try_lock`.
40pub enum SpinTryLockError {
41 /// The lock could not be acquired at this time because the operation would otherwise block.
42 WouldBlock,
43}
44
45/// A lower-level mutual exclusion primitive for protecting data.
46///
47/// This is modeled after [`sync::Mutex`] but instead of using `k_mutex` from Zephyr, it uses
48/// `k_spinlock`. It's main advantage is that it is usable from IRQ context. However, it also is
49/// uninterruptible, and prevents even IRQ handlers from running.
50///
51/// [`sync::Mutex`]: crate::sync::Mutex
52pub struct SpinMutex<T: ?Sized> {
53 inner: UnsafeCell<raw::k_spinlock>,
54 data: UnsafeCell<T>,
55}
56
57/// As the data is protected by spinlocks, with RAII ensuring the lock is always released, this
58/// satisfies Rust's requirements for Send and Sync. The dependency of both on "Send" of the data
59/// type is intentional, as it is the Mutex that is providing the Sync semantics. However, it only
60/// makes sense for types that are usable from multiple thread contexts.
61unsafe impl<T: ?Sized + Send> Send for SpinMutex<T> {}
62unsafe impl<T: ?Sized + Send> Sync for SpinMutex<T> {}
63
64impl<T> fmt::Debug for SpinMutex<T> {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 write!(f, "SpinMutex {:?}", self.inner)
67 }
68}
69
70/// An RAII implementation of a "scoped lock" of a SpinMutex. When this structure is dropped (falls
71/// out of scope), the lock will be unlocked.
72///
73/// The data protected by the SpinMutex can be accessed through this guard via it's [`Deref'] and
74/// [`DerefMut`] implementations.
75///
76/// This structure is created by the [`lock`] and [`try_lock`] methods on [`SpinMutex`].
77///
78/// [`lock`]: SpinMutex::lock
79/// [`try_lock`]: SpinMutex::try_lock
80///
81/// Borrowed largely from std's `MutexGuard`, but adapted to use spinlocks.
82pub struct SpinMutexGuard<'a, T: ?Sized + 'a> {
83 lock: &'a SpinMutex<T>,
84 key: raw::k_spinlock_key_t,
85 // Mark as not Send.
86 _nosend: PhantomData<UnsafeCell<()>>,
87}
88
89// Negative trait bounds are unstable, see the _nosend field above.
90/// Mark as Sync if the contained data is sync.
91unsafe impl<T: ?Sized + Sync> Sync for SpinMutexGuard<'_, T> {}
92
93impl<T> SpinMutex<T> {
94 /// Construct a new wrapped Mutex.
95 pub const fn new(t: T) -> SpinMutex<T> {
96 SpinMutex {
97 inner: UnsafeCell::new(unsafe { core::mem::zeroed() }),
98 data: UnsafeCell::new(t),
99 }
100 }
101}
102
103impl<T: ?Sized> SpinMutex<T> {
104 /// Acquire a mutex, spinning as needed to aquire the controlling spinlock.
105 ///
106 /// This function will spin the current thread until it is able to acquire the spinlock.
107 /// Returns an RAII guard to allow scoped unlock of the lock. When the guard goes out of scope,
108 /// the SpinMutex will be unlocked.
109 pub fn lock(&self) -> SpinLockResult<SpinMutexGuard<'_, T>> {
110 let key = unsafe { raw::k_spin_lock(self.inner.get()) };
111 unsafe { Ok(SpinMutexGuard::new(self, key)) }
112 }
113
114 /// Attempts to aquire this lock.
115 ///
116 /// If the lock could not be aquired at this time, then [`Err`] is returned. Otherwise an RAII
117 /// guard is returned. The lock will be unlocked when the guard is dropped.
118 ///
119 /// This function does not block.
120 pub fn try_lock(&self) -> SpinTryLockResult<SpinMutexGuard<'_, T>> {
121 let mut key = raw::k_spinlock_key_t { key: 0 };
122 match unsafe { raw::k_spin_trylock(self.inner.get(), &mut key) } {
123 0 => unsafe { Ok(SpinMutexGuard::new(self, key)) },
124 _ => Err(SpinTryLockError::WouldBlock),
125 }
126 }
127}
128
129impl<'mutex, T: ?Sized> SpinMutexGuard<'mutex, T> {
130 unsafe fn new(
131 lock: &'mutex SpinMutex<T>,
132 key: raw::k_spinlock_key_t,
133 ) -> SpinMutexGuard<'mutex, T> {
134 SpinMutexGuard {
135 lock,
136 key,
137 _nosend: PhantomData,
138 }
139 }
140}
141
142impl<T: ?Sized> Deref for SpinMutexGuard<'_, T> {
143 type Target = T;
144
145 fn deref(&self) -> &T {
146 unsafe { &*self.lock.data.get() }
147 }
148}
149
150impl<T: ?Sized> DerefMut for SpinMutexGuard<'_, T> {
151 fn deref_mut(&mut self) -> &mut T {
152 unsafe { &mut *self.lock.data.get() }
153 }
154}
155
156impl<T: ?Sized> Drop for SpinMutexGuard<'_, T> {
157 #[inline]
158 fn drop(&mut self) {
159 unsafe {
160 raw::k_spin_unlock(self.lock.inner.get(), self.key);
161 }
162 }
163}