zephyr/sync/mutex.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 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
//! Higher level Mutex type and friends.
//!
//! These are modeled after the synchronization primitives in
//! [`std::sync`](https://doc.rust-lang.org/stable/std/sync/index.html), notably `Mutex`, and
//! `Condvar`, and the associated types.
use core::{
cell::UnsafeCell,
convert::Infallible,
fmt,
marker::PhantomData,
ops::{Deref, DerefMut},
};
use crate::sys::sync as sys;
use crate::time::{Forever, NoWait};
/// Until poisoning is implemented, mutexes never return an error, and we just get back the guard.
pub type LockResult<Guard> = Result<Guard, Infallible>;
/// The return type from [`Mutex::try_lock`].
///
/// The error indicates the reason for the failure. Until poisoning is
/// implemented, there is only a single type of failure.
pub type TryLockResult<Guard> = Result<Guard, TryLockError>;
/// An enumeration of possible errors associated with a [`TryLockResult`].
///
/// Note that until Poisoning is implemented, there is only one value of this.
pub enum TryLockError {
/// The lock could not be acquired at this time because the operation would otherwise block.
WouldBlock,
}
/// A mutual exclusion primitive useful for protecting shared data.
///
/// This mutex will block threads waiting for the lock to become available. This is modeled after
/// [`std::sync::Mutex`](https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html), and attempts
/// to implement that API as closely as makes sense on Zephyr. Currently, it has the following
/// differences:
/// - Poisoning: This does not yet implement poisoning, as there is no way to recover from panic at
/// this time on Zephyr.
/// - Allocation: `new` is not yet provided, and will be provided once kernel object pools are
/// implemented. Please use `new_from` which takes a reference to a statically allocated
/// `sys::Mutex`.
pub struct Mutex<T: ?Sized> {
inner: sys::Mutex,
// poison: ...
data: UnsafeCell<T>,
}
// At least if correctly done, the Mutex provides for Send and Sync as long as the inner data
// supports Send.
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
impl<T> fmt::Debug for Mutex<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Mutex {:?}", self.inner)
}
}
/// An RAII implementation of a "scoped lock" of a mutex. When this structure is dropped (faslls
/// out of scope), the lock will be unlocked.
///
/// The data protected by the mutex can be accessed through this guard via its [`Deref`] and
/// [`DerefMut`] implementations.
///
/// This structure is created by the [`lock`] and [`try_lock`] methods on [`Mutex`].
///
/// [`lock`]: Mutex::lock
/// [`try_lock`]: Mutex::try_lock
///
/// Taken directly from
/// [`std::sync::MutexGuard`](https://doc.rust-lang.org/stable/std/sync/struct.MutexGuard.html).
pub struct MutexGuard<'a, T: ?Sized + 'a> {
lock: &'a Mutex<T>,
// until <https://github.com/rust-lang/rust/issues/68318> is implemented, we have to mark unsend
// explicitly. This can be done by holding Phantom data with an unsafe cell in it.
_nosend: PhantomData<UnsafeCell<()>>,
}
// Make sure the guard doesn't get sent.
// Negative trait bounds are unstable, see marker above.
// impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
impl<T> Mutex<T> {
/// Construct a new wrapped Mutex, using the given underlying sys mutex. This is different that
/// `std::sync::Mutex` in that in Zephyr, objects are frequently allocated statically, and the
/// sys Mutex will be taken by this structure. It is safe to share the underlying Mutex between
/// different items, but without careful use, it is easy to deadlock, so it is not recommended.
pub const fn new_from(t: T, raw_mutex: sys::Mutex) -> Mutex<T> {
Mutex {
inner: raw_mutex,
data: UnsafeCell::new(t),
}
}
/// Construct a new Mutex, dynamically allocating the underlying sys Mutex.
pub const fn new(t: T) -> Mutex<T> {
Mutex::new_from(t, sys::Mutex::new())
}
}
impl<T: ?Sized> Mutex<T> {
/// Acquires a mutex, blocking the current thread until it is able to do so.
///
/// This function will block the local thread until it is available to acquire the mutex. Upon
/// returning, the thread is the only thread with the lock held. An RAII guard is returned to
/// allow scoped unlock of the lock. When the guard goes out of scope, the mutex will be
/// unlocked.
///
/// In `std`, an attempt to lock a mutex by a thread that already holds the mutex is
/// unspecified. Zephyr explicitly supports this behavior, by simply incrementing a lock
/// count.
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
// With `Forever`, should never return an error.
self.inner.lock(Forever).unwrap();
unsafe { Ok(MutexGuard::new(self)) }
}
/// Attempts to acquire this lock.
///
/// If the lock could not be acquired 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) -> TryLockResult<MutexGuard<'_, T>> {
match self.inner.lock(NoWait) {
Ok(()) => unsafe { Ok(MutexGuard::new(self)) },
// TODO: It might be better to distinguish these errors, and only return the WouldBlock
// if that is the corresponding error. But, the lock shouldn't fail in Zephyr.
Err(_) => Err(TryLockError::WouldBlock),
}
}
}
impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
unsafe fn new(lock: &'mutex Mutex<T>) -> MutexGuard<'mutex, T> {
// poison todo
MutexGuard {
lock,
_nosend: PhantomData,
}
}
}
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.lock.data.get() }
}
}
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.lock.data.get() }
}
}
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
#[inline]
fn drop(&mut self) {
if let Err(e) = self.lock.inner.unlock() {
panic!("Problem unlocking MutexGuard in drop: {:?}", e);
}
}
}
/// Inspired by
/// [`std::sync::Condvar`](https://doc.rust-lang.org/stable/std/sync/struct.Condvar.html),
/// implemented directly using `z_condvar` in Zephyr.
///
/// Condition variables represent the ability to block a thread such that it consumes no CPU time
/// while waiting for an even to occur. Condition variables are typically associated with a
/// boolean predicate (a condition) and a mutex. The predicate is always verified inside of the
/// mutex before determining that a thread must block.
///
/// Functions in this module will block the current **thread** of execution. Note that any attempt
/// to use multiple mutexces on the same condition variable may result in a runtime panic.
pub struct Condvar {
inner: sys::Condvar,
}
impl Condvar {
/// Construct a new wrapped Condvar, using the given underlying `k_condvar`.
///
/// This is different from `std::sync::Condvar` in that in Zephyr, objects are frequently
/// allocated statically, and the sys Condvar will be taken by this structure.
pub const fn new_from(raw_condvar: sys::Condvar) -> Condvar {
Condvar { inner: raw_condvar }
}
/// Construct a new Condvar, dynamically allocating the underlying Zephyr `k_condvar`.
#[cfg(CONFIG_RUST_ALLOC)]
pub fn new() -> Condvar {
Condvar::new_from(sys::Condvar::new())
}
/// Blocks the current thread until this conditional variable receives a notification.
///
/// This function will automatically unlock the mutex specified (represented by `guard`) and
/// block the current thread. This means that any calls to `notify_one` or `notify_all` which
/// happen logically after the mutex is unlocked are candidates to wake this thread up. When
/// this function call returns, the lock specified will have been re-equired.
///
/// Note that this function is susceptable to spurious wakeups. Condition variables normally
/// have a boolean predicate associated with them, and the predicate must always be checked
/// each time this function returns to protect against spurious wakeups.
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> {
self.inner.wait(&guard.lock.inner);
Ok(guard)
}
// TODO: wait_while
// TODO: wait_timeout_ms
// TODO: wait_timeout
// TODO: wait_timeout_while
/// Wakes up one blocked thread on this condvar.
///
/// If there is a blocked thread on this condition variable, then it will be woken up from its
/// call to `wait` or `wait_timeout`. Calls to `notify_one` are not buffered in any way.
///
/// To wakeup all threads, see `notify_all`.
pub fn notify_one(&self) {
self.inner.notify_one();
}
/// Wakes up all blocked threads on this condvar.
///
/// This methods will ensure that any current waiters on the condition variable are awoken.
/// Calls to `notify_all()` are not buffered in any way.
///
/// To wake up only one thread, see `notify_one`.
pub fn notify_all(&self) {
self.inner.notify_all();
}
}
#[cfg(CONFIG_RUST_ALLOC)]
impl Default for Condvar {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for Condvar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Condvar {:?}", self.inner)
}
}