zephyr/sync/mutex.rs
1//! Higher level Mutex type and friends.
2//!
3//! These are modeled after the synchronization primitives in
4//! [`std::sync`](https://doc.rust-lang.org/stable/std/sync/index.html), notably `Mutex`, and
5//! `Condvar`, and the associated types.
6
7use core::{
8 cell::UnsafeCell,
9 convert::Infallible,
10 fmt,
11 marker::PhantomData,
12 ops::{Deref, DerefMut},
13};
14
15use crate::sync::atomic::{AtomicBool, Ordering};
16use crate::sys::sync as sys;
17use crate::time::{Forever, NoWait};
18
19/// Until poisoning is implemented, mutexes never return an error, and we just get back the guard.
20pub type LockResult<Guard> = Result<Guard, Infallible>;
21
22/// The return type from [`Mutex::try_lock`].
23///
24/// The error indicates the reason for the failure. Until poisoning is
25/// implemented, there is only a single type of failure.
26pub type TryLockResult<Guard> = Result<Guard, TryLockError>;
27
28/// An enumeration of possible errors associated with a [`TryLockResult`].
29///
30/// Note that until Poisoning is implemented, there is only one value of this.
31pub enum TryLockError {
32 /// The lock could not be acquired at this time because the operation would otherwise block.
33 WouldBlock,
34}
35
36/// A mutual exclusion primitive useful for protecting shared data.
37///
38/// This mutex will block threads waiting for the lock to become available. This is modeled after
39/// [`std::sync::Mutex`](https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html), and attempts
40/// to implement that API as closely as makes sense on Zephyr. Currently, it has the following
41/// differences:
42/// - Poisoning: This does not yet implement poisoning, as there is no way to recover from panic at
43/// this time on Zephyr.
44/// - Allocation: `new` is not yet provided, and will be provided once kernel object pools are
45/// implemented. Please use `new_from` which takes a reference to a statically allocated
46/// `sys::Mutex`.
47pub struct Mutex<T: ?Sized> {
48 inner: sys::Mutex,
49 // poison: ...
50 locked: AtomicBool,
51 data: UnsafeCell<T>,
52}
53
54// At least if correctly done, the Mutex provides for Send and Sync as long as the inner data
55// supports Send.
56unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
57unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
58
59impl<T> fmt::Debug for Mutex<T> {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(f, "Mutex {:?}", self.inner)
62 }
63}
64
65/// An RAII implementation of a "scoped lock" of a mutex. When this structure is dropped (faslls
66/// out of scope), the lock will be unlocked.
67///
68/// The data protected by the mutex can be accessed through this guard via its [`Deref`] and
69/// [`DerefMut`] implementations.
70///
71/// This structure is created by the [`lock`] and [`try_lock`] methods on [`Mutex`].
72///
73/// [`lock`]: Mutex::lock
74/// [`try_lock`]: Mutex::try_lock
75///
76/// Taken directly from
77/// [`std::sync::MutexGuard`](https://doc.rust-lang.org/stable/std/sync/struct.MutexGuard.html).
78pub struct MutexGuard<'a, T: ?Sized + 'a> {
79 lock: &'a Mutex<T>,
80 // until <https://github.com/rust-lang/rust/issues/68318> is implemented, we have to mark unsend
81 // explicitly. This can be done by holding Phantom data with a pointer in it.
82 _nosend: PhantomData<*const ()>,
83}
84
85// Make sure the guard doesn't get sent.
86// Negative trait bounds are unstable, see marker above.
87// impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
88unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
89
90impl<T> Mutex<T> {
91 /// Construct a new wrapped Mutex, using the given underlying sys mutex. This is different that
92 /// `std::sync::Mutex` in that in Zephyr, objects are frequently allocated statically, and the
93 /// sys Mutex will be taken by this structure. It is safe to share the underlying Mutex between
94 /// different items, but without careful use, it is easy to deadlock, so it is not recommended.
95 pub const fn new_from(t: T, raw_mutex: sys::Mutex) -> Mutex<T> {
96 Mutex {
97 inner: raw_mutex,
98 locked: AtomicBool::new(false),
99 data: UnsafeCell::new(t),
100 }
101 }
102
103 /// Construct a new Mutex, dynamically allocating the underlying sys Mutex.
104 pub const fn new(t: T) -> Mutex<T> {
105 Mutex::new_from(t, sys::Mutex::new())
106 }
107}
108
109impl<T: ?Sized> Mutex<T> {
110 /// Acquires a mutex, blocking the current thread until it is able to do so.
111 ///
112 /// This function will block the local thread until it is available to acquire the mutex. Upon
113 /// returning, the thread is the only thread with the lock held. An RAII guard is returned to
114 /// allow scoped unlock of the lock. When the guard goes out of scope, the mutex will be
115 /// unlocked.
116 ///
117 /// Attempting to lock a mutex that is already held by the current thread is not permitted and
118 /// will panic. Because [`MutexGuard`] provides mutable access to the protected data via
119 /// [`DerefMut`], allowing reentrancy would produce multiple mutable references to the same
120 /// data, which is undefined behavior in Rust.
121 pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
122 // With `Forever`, should never return an error.
123 self.inner.lock(Forever).unwrap();
124 if self.locked.swap(true, Ordering::Acquire) {
125 self.inner.unlock().unwrap();
126 panic!("cannot recursively lock Mutex");
127 }
128 unsafe { Ok(MutexGuard::new(self)) }
129 }
130
131 /// Attempts to acquire this lock.
132 ///
133 /// If the lock could not be acquired at this time, then [`Err`] is returned. Otherwise, an RAII
134 /// guard is returned. The lock will be unlocked when the guard is dropped.
135 ///
136 /// This function does not block. Attempting to lock a mutex that is already held by the current
137 /// thread will return `Err(TryLockError::WouldBlock)`.
138 pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> {
139 match self.inner.lock(NoWait) {
140 Ok(()) => {
141 if self.locked.swap(true, Ordering::Acquire) {
142 self.inner.unlock().unwrap();
143 Err(TryLockError::WouldBlock)
144 } else {
145 unsafe { Ok(MutexGuard::new(self)) }
146 }
147 }
148 // TODO: It might be better to distinguish these errors, and only return the WouldBlock
149 // if that is the corresponding error. But, the lock shouldn't fail in Zephyr.
150 Err(_) => Err(TryLockError::WouldBlock),
151 }
152 }
153}
154
155impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
156 unsafe fn new(lock: &'mutex Mutex<T>) -> MutexGuard<'mutex, T> {
157 // poison todo
158 MutexGuard {
159 lock,
160 _nosend: PhantomData,
161 }
162 }
163}
164
165impl<T: ?Sized> Deref for MutexGuard<'_, T> {
166 type Target = T;
167
168 fn deref(&self) -> &T {
169 unsafe { &*self.lock.data.get() }
170 }
171}
172
173impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
174 fn deref_mut(&mut self) -> &mut T {
175 unsafe { &mut *self.lock.data.get() }
176 }
177}
178
179impl<T: ?Sized> Drop for MutexGuard<'_, T> {
180 #[inline]
181 fn drop(&mut self) {
182 self.lock.locked.store(false, Ordering::Release);
183 if let Err(e) = self.lock.inner.unlock() {
184 panic!("Problem unlocking MutexGuard in drop: {:?}", e);
185 }
186 }
187}
188
189/// Inspired by
190/// [`std::sync::Condvar`](https://doc.rust-lang.org/stable/std/sync/struct.Condvar.html),
191/// implemented directly using `z_condvar` in Zephyr.
192///
193/// Condition variables represent the ability to block a thread such that it consumes no CPU time
194/// while waiting for an even to occur. Condition variables are typically associated with a
195/// boolean predicate (a condition) and a mutex. The predicate is always verified inside of the
196/// mutex before determining that a thread must block.
197///
198/// Functions in this module will block the current **thread** of execution. Note that any attempt
199/// to use multiple mutexces on the same condition variable may result in a runtime panic.
200pub struct Condvar {
201 inner: sys::Condvar,
202}
203
204impl Condvar {
205 /// Construct a new wrapped Condvar, using the given underlying `k_condvar`.
206 ///
207 /// This is different from `std::sync::Condvar` in that in Zephyr, objects are frequently
208 /// allocated statically, and the sys Condvar will be taken by this structure.
209 pub const fn new_from(raw_condvar: sys::Condvar) -> Condvar {
210 Condvar { inner: raw_condvar }
211 }
212
213 /// Construct a new Condvar, dynamically allocating the underlying Zephyr `k_condvar`.
214 #[cfg(CONFIG_RUST_ALLOC)]
215 pub fn new() -> Condvar {
216 Condvar::new_from(sys::Condvar::new())
217 }
218
219 /// Blocks the current thread until this conditional variable receives a notification.
220 ///
221 /// This function will automatically unlock the mutex specified (represented by `guard`) and
222 /// block the current thread. This means that any calls to `notify_one` or `notify_all` which
223 /// happen logically after the mutex is unlocked are candidates to wake this thread up. When
224 /// this function call returns, the lock specified will have been re-equired.
225 ///
226 /// Note that this function is susceptable to spurious wakeups. Condition variables normally
227 /// have a boolean predicate associated with them, and the predicate must always be checked
228 /// each time this function returns to protect against spurious wakeups.
229 pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> {
230 guard.lock.locked.store(false, Ordering::Release);
231 self.inner.wait(&guard.lock.inner);
232 guard.lock.locked.store(true, Ordering::Release);
233 Ok(guard)
234 }
235
236 // TODO: wait_while
237 // TODO: wait_timeout_ms
238 // TODO: wait_timeout
239 // TODO: wait_timeout_while
240
241 /// Wakes up one blocked thread on this condvar.
242 ///
243 /// If there is a blocked thread on this condition variable, then it will be woken up from its
244 /// call to `wait` or `wait_timeout`. Calls to `notify_one` are not buffered in any way.
245 ///
246 /// To wakeup all threads, see `notify_all`.
247 pub fn notify_one(&self) {
248 self.inner.notify_one();
249 }
250
251 /// Wakes up all blocked threads on this condvar.
252 ///
253 /// This methods will ensure that any current waiters on the condition variable are awoken.
254 /// Calls to `notify_all()` are not buffered in any way.
255 ///
256 /// To wake up only one thread, see `notify_one`.
257 pub fn notify_all(&self) {
258 self.inner.notify_all();
259 }
260}
261
262#[cfg(CONFIG_RUST_ALLOC)]
263impl Default for Condvar {
264 fn default() -> Self {
265 Self::new()
266 }
267}
268
269impl fmt::Debug for Condvar {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 write!(f, "Condvar {:?}", self.inner)
272 }
273}