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