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}