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}