zephyr/kio/sync.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
//! Synchronization mechanisms that work with async.
//!
//! Notably, Zephyr's `k_mutex` type isn't supported as a type that can be waited for
//! asynchronously.
//!
//! The main problem with `k_mutex` (meaning [`crate::sync::Mutex`]) is that the `lock` operation
//! can block, and since multiple tasks may be scheduled for the same work queue, the system can
//! deadlock, as the scheduler may not run to allow the task that actually holds the mutex to run.
//!
//! As an initial stopgap. We provide a [`Mutex`] type that is usable within an async context. We
//! do not currently implement an associated `Condvar`.
//!
//! Note that using Semaphores for locking means that this mechanism doesn't handle priority
//! inversion issues. Be careful with workers that run at different priorities.
// Use the same error types from the regular sync version.
use core::{
cell::UnsafeCell,
fmt,
marker::PhantomData,
ops::{Deref, DerefMut},
};
use crate::{
sync::{LockResult, TryLockError, TryLockResult},
sys::sync::Semaphore,
time::{Forever, NoWait},
};
/// A mutual exclusion primitive useful for protecting shared data. Async version.
///
/// This mutex will block a task waiting for the lock to become available.
pub struct Mutex<T: ?Sized> {
/// The semaphore indicating ownership of the data. When it is "0" the task that did the 'take'
/// on it owns the data, and will use `give` when it is unlocked. This mechanism works for
/// simple Mutex that protects the data without needing a condition variable.
inner: Semaphore,
data: UnsafeCell<T>,
}
// SAFETY: The semaphore, with the semantics provided here, provide Send and Sync.
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 held lock.
pub struct MutexGuard<'a, T: ?Sized + 'a> {
lock: &'a Mutex<T>,
// Mark !Send explicitly until support is added to Rust for this.
_nosend: PhantomData<UnsafeCell<()>>,
}
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
impl<T> Mutex<T> {
/// Construct a new Mutex.
pub fn new(t: T) -> Mutex<T> {
Mutex {
inner: Semaphore::new(1, 1),
data: UnsafeCell::new(t),
}
}
}
impl<T: ?Sized> Mutex<T> {
/// Acquire the mutex, blocking the current thread until it is able to do so.
///
/// This is a sync version, and calling it from an async task will possibly block the async work
/// thread, potentially causing deadlock.
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
self.inner.take(Forever).unwrap();
unsafe { Ok(MutexGuard::new(self)) }
}
/// Aquire the mutex, async version.
pub async fn lock_async(&self) -> LockResult<MutexGuard<'_, T>> {
self.inner.take_async(Forever).await.unwrap();
unsafe { Ok(MutexGuard::new(self)) }
}
/// Attempt to aquire the lock.
pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> {
match self.inner.take(NoWait) {
Ok(()) => unsafe { Ok(MutexGuard::new(self)) },
// TODO: Distinguish timeout from other errors.
Err(_) => Err(TryLockError::WouldBlock),
}
}
}
impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
unsafe fn new(lock: &'mutex Mutex<T>) -> MutexGuard<'mutex, T> {
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) {
self.lock.inner.give();
}
}