zephyr/sync/
mutex.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
//! Higher level Mutex type and friends.
//!
//! These are modeled after the synchronization primitives in
//! [`std::sync`](https://doc.rust-lang.org/stable/std/sync/index.html), notably `Mutex`, and
//! `Condvar`, and the associated types.

use core::{
    cell::UnsafeCell,
    convert::Infallible,
    fmt,
    marker::PhantomData,
    ops::{Deref, DerefMut},
};

use crate::sys::sync as sys;
use crate::time::{Forever, NoWait};

/// Until poisoning is implemented, mutexes never return an error, and we just get back the guard.
pub type LockResult<Guard> = Result<Guard, Infallible>;

/// The return type from [`Mutex::try_lock`].
///
/// The error indicates the reason for the failure.  Until poisoning is
/// implemented, there is only a single type of failure.
pub type TryLockResult<Guard> = Result<Guard, TryLockError>;

/// An enumeration of possible errors associated with a [`TryLockResult`].
///
/// Note that until Poisoning is implemented, there is only one value of this.
pub enum TryLockError {
    /// The lock could not be acquired at this time because the operation would otherwise block.
    WouldBlock,
}

/// A mutual exclusion primitive useful for protecting shared data.
///
/// This mutex will block threads waiting for the lock to become available. This is modeled after
/// [`std::sync::Mutex`](https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html), and attempts
/// to implement that API as closely as makes sense on Zephyr.  Currently, it has the following
/// differences:
/// - Poisoning: This does not yet implement poisoning, as there is no way to recover from panic at
///   this time on Zephyr.
/// - Allocation: `new` is not yet provided, and will be provided once kernel object pools are
///   implemented.  Please use `new_from` which takes a reference to a statically allocated
///   `sys::Mutex`.
pub struct Mutex<T: ?Sized> {
    inner: sys::Mutex,
    // poison: ...
    data: UnsafeCell<T>,
}

// At least if correctly done, the Mutex provides for Send and Sync as long as the inner data
// supports Send.
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 "scoped lock" of a mutex.  When this structure is dropped (faslls
/// out of scope), the lock will be unlocked.
///
/// The data protected by the mutex can be accessed through this guard via its [`Deref`] and
/// [`DerefMut`] implementations.
///
/// This structure is created by the [`lock`] and [`try_lock`] methods on [`Mutex`].
///
/// [`lock`]: Mutex::lock
/// [`try_lock`]: Mutex::try_lock
///
/// Taken directly from
/// [`std::sync::MutexGuard`](https://doc.rust-lang.org/stable/std/sync/struct.MutexGuard.html).
pub struct MutexGuard<'a, T: ?Sized + 'a> {
    lock: &'a Mutex<T>,
    // until <https://github.com/rust-lang/rust/issues/68318> is implemented, we have to mark unsend
    // explicitly.  This can be done by holding Phantom data with an unsafe cell in it.
    _nosend: PhantomData<UnsafeCell<()>>,
}

// Make sure the guard doesn't get sent.
// Negative trait bounds are unstable, see marker above.
// impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}

impl<T> Mutex<T> {
    /// Construct a new wrapped Mutex, using the given underlying sys mutex.  This is different that
    /// `std::sync::Mutex` in that in Zephyr, objects are frequently allocated statically, and the
    /// sys Mutex will be taken by this structure.  It is safe to share the underlying Mutex between
    /// different items, but without careful use, it is easy to deadlock, so it is not recommended.
    pub const fn new_from(t: T, raw_mutex: sys::Mutex) -> Mutex<T> {
        Mutex {
            inner: raw_mutex,
            data: UnsafeCell::new(t),
        }
    }

    /// Construct a new Mutex, dynamically allocating the underlying sys Mutex.
    pub const fn new(t: T) -> Mutex<T> {
        Mutex::new_from(t, sys::Mutex::new())
    }
}

impl<T: ?Sized> Mutex<T> {
    /// Acquires a mutex, blocking the current thread until it is able to do so.
    ///
    /// This function will block the local thread until it is available to acquire the mutex. Upon
    /// returning, the thread is the only thread with the lock held. An RAII guard is returned to
    /// allow scoped unlock of the lock. When the guard goes out of scope, the mutex will be
    /// unlocked.
    ///
    /// In `std`, an attempt to lock a mutex by a thread that already holds the mutex is
    /// unspecified.  Zephyr explicitly supports this behavior, by simply incrementing a lock
    /// count.
    pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
        // With `Forever`, should never return an error.
        self.inner.lock(Forever).unwrap();
        unsafe { Ok(MutexGuard::new(self)) }
    }

    /// Attempts to acquire this lock.
    ///
    /// If the lock could not be acquired at this time, then [`Err`] is returned. Otherwise, an RAII
    /// guard is returned. The lock will be unlocked when the guard is dropped.
    ///
    /// This function does not block.
    pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> {
        match self.inner.lock(NoWait) {
            Ok(()) => unsafe { Ok(MutexGuard::new(self)) },
            // TODO: It might be better to distinguish these errors, and only return the WouldBlock
            // if that is the corresponding error. But, the lock shouldn't fail in Zephyr.
            Err(_) => Err(TryLockError::WouldBlock),
        }
    }
}

impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
    unsafe fn new(lock: &'mutex Mutex<T>) -> MutexGuard<'mutex, T> {
        // poison todo
        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) {
        if let Err(e) = self.lock.inner.unlock() {
            panic!("Problem unlocking MutexGuard in drop: {:?}", e);
        }
    }
}

/// Inspired by
/// [`std::sync::Condvar`](https://doc.rust-lang.org/stable/std/sync/struct.Condvar.html),
/// implemented directly using `z_condvar` in Zephyr.
///
/// Condition variables represent the ability to block a thread such that it consumes no CPU time
/// while waiting for an even to occur.  Condition variables are typically associated with a
/// boolean predicate (a condition) and a mutex.  The predicate is always verified inside of the
/// mutex before determining that a thread must block.
///
/// Functions in this module will block the current **thread** of execution.  Note that any attempt
/// to use multiple mutexces on the same condition variable may result in a runtime panic.
pub struct Condvar {
    inner: sys::Condvar,
}

impl Condvar {
    /// Construct a new wrapped Condvar, using the given underlying `k_condvar`.
    ///
    /// This is different from `std::sync::Condvar` in that in Zephyr, objects are frequently
    /// allocated statically, and the sys Condvar will be taken by this structure.
    pub const fn new_from(raw_condvar: sys::Condvar) -> Condvar {
        Condvar { inner: raw_condvar }
    }

    /// Construct a new Condvar, dynamically allocating the underlying Zephyr `k_condvar`.
    #[cfg(CONFIG_RUST_ALLOC)]
    pub fn new() -> Condvar {
        Condvar::new_from(sys::Condvar::new())
    }

    /// Blocks the current thread until this conditional variable receives a notification.
    ///
    /// This function will automatically unlock the mutex specified (represented by `guard`) and
    /// block the current thread.  This means that any calls to `notify_one` or `notify_all` which
    /// happen logically after the mutex is unlocked are candidates to wake this thread up.  When
    /// this function call returns, the lock specified will have been re-equired.
    ///
    /// Note that this function is susceptable to spurious wakeups.  Condition variables normally
    /// have a boolean predicate associated with them, and the predicate must always be checked
    /// each time this function returns to protect against spurious wakeups.
    pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> {
        self.inner.wait(&guard.lock.inner);
        Ok(guard)
    }

    // TODO: wait_while
    // TODO: wait_timeout_ms
    // TODO: wait_timeout
    // TODO: wait_timeout_while

    /// Wakes up one blocked thread on this condvar.
    ///
    /// If there is a blocked thread on this condition variable, then it will be woken up from its
    /// call to `wait` or `wait_timeout`. Calls to `notify_one` are not buffered in any way.
    ///
    /// To wakeup all threads, see `notify_all`.
    pub fn notify_one(&self) {
        self.inner.notify_one();
    }

    /// Wakes up all blocked threads on this condvar.
    ///
    /// This methods will ensure that any current waiters on the condition variable are awoken.
    /// Calls to `notify_all()` are not buffered in any way.
    ///
    /// To wake up only one thread, see `notify_one`.
    pub fn notify_all(&self) {
        self.inner.notify_all();
    }
}

#[cfg(CONFIG_RUST_ALLOC)]
impl Default for Condvar {
    fn default() -> Self {
        Self::new()
    }
}

impl fmt::Debug for Condvar {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Condvar {:?}", self.inner)
    }
}