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