Skip to main content

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}