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