aboutsummaryrefslogtreecommitdiff
path: root/embassy-sync/src
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-sync/src')
-rw-r--r--embassy-sync/src/channel.rs27
-rw-r--r--embassy-sync/src/mutex.rs133
2 files changed, 160 insertions, 0 deletions
diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs
index 48f4dafd6..18be462cb 100644
--- a/embassy-sync/src/channel.rs
+++ b/embassy-sync/src/channel.rs
@@ -449,6 +449,18 @@ impl<T, const N: usize> ChannelState<T, N> {
449 Poll::Pending 449 Poll::Pending
450 } 450 }
451 } 451 }
452
453 fn len(&self) -> usize {
454 self.queue.len()
455 }
456
457 fn is_empty(&self) -> bool {
458 self.queue.is_empty()
459 }
460
461 fn is_full(&self) -> bool {
462 self.queue.is_full()
463 }
452} 464}
453 465
454/// A bounded channel for communicating between asynchronous tasks 466/// A bounded channel for communicating between asynchronous tasks
@@ -572,6 +584,21 @@ where
572 pub fn try_receive(&self) -> Result<T, TryReceiveError> { 584 pub fn try_receive(&self) -> Result<T, TryReceiveError> {
573 self.lock(|c| c.try_receive()) 585 self.lock(|c| c.try_receive())
574 } 586 }
587
588 /// Returns the number of elements currently in the channel.
589 pub fn len(&self) -> usize {
590 self.lock(|c| c.len())
591 }
592
593 /// Returns whether the channel is empty.
594 pub fn is_empty(&self) -> bool {
595 self.lock(|c| c.is_empty())
596 }
597
598 /// Returns whether the channel is full.
599 pub fn is_full(&self) -> bool {
600 self.lock(|c| c.is_full())
601 }
575} 602}
576 603
577/// Implements the DynamicChannel to allow creating types that are unaware of the queue size with the 604/// Implements the DynamicChannel to allow creating types that are unaware of the queue size with the
diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs
index 72459d660..b48a408c4 100644
--- a/embassy-sync/src/mutex.rs
+++ b/embassy-sync/src/mutex.rs
@@ -3,6 +3,7 @@
3//! This module provides a mutex that can be used to synchronize data between asynchronous tasks. 3//! This module provides a mutex that can be used to synchronize data between asynchronous tasks.
4use core::cell::{RefCell, UnsafeCell}; 4use core::cell::{RefCell, UnsafeCell};
5use core::future::poll_fn; 5use core::future::poll_fn;
6use core::mem;
6use core::ops::{Deref, DerefMut}; 7use core::ops::{Deref, DerefMut};
7use core::task::Poll; 8use core::task::Poll;
8 9
@@ -134,6 +135,7 @@ where
134/// successfully locked the mutex, and grants access to the contents. 135/// successfully locked the mutex, and grants access to the contents.
135/// 136///
136/// Dropping it unlocks the mutex. 137/// Dropping it unlocks the mutex.
138#[clippy::has_significant_drop]
137pub struct MutexGuard<'a, M, T> 139pub struct MutexGuard<'a, M, T>
138where 140where
139 M: RawMutex, 141 M: RawMutex,
@@ -142,6 +144,25 @@ where
142 mutex: &'a Mutex<M, T>, 144 mutex: &'a Mutex<M, T>,
143} 145}
144 146
147impl<'a, M, T> MutexGuard<'a, M, T>
148where
149 M: RawMutex,
150 T: ?Sized,
151{
152 /// Returns a locked view over a portion of the locked data.
153 pub fn map<U>(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> {
154 let mutex = this.mutex;
155 let value = fun(unsafe { &mut *this.mutex.inner.get() });
156 // Don't run the `drop` method for MutexGuard. The ownership of the underlying
157 // locked state is being moved to the returned MappedMutexGuard.
158 mem::forget(this);
159 MappedMutexGuard {
160 state: &mutex.state,
161 value,
162 }
163 }
164}
165
145impl<'a, M, T> Drop for MutexGuard<'a, M, T> 166impl<'a, M, T> Drop for MutexGuard<'a, M, T>
146where 167where
147 M: RawMutex, 168 M: RawMutex,
@@ -180,3 +201,115 @@ where
180 unsafe { &mut *(self.mutex.inner.get()) } 201 unsafe { &mut *(self.mutex.inner.get()) }
181 } 202 }
182} 203}
204
205/// A handle to a held `Mutex` that has had a function applied to it via [`MutexGuard::map`] or
206/// [`MappedMutexGuard::map`].
207///
208/// This can be used to hold a subfield of the protected data.
209#[clippy::has_significant_drop]
210pub struct MappedMutexGuard<'a, M, T>
211where
212 M: RawMutex,
213 T: ?Sized,
214{
215 state: &'a BlockingMutex<M, RefCell<State>>,
216 value: *mut T,
217}
218
219impl<'a, M, T> MappedMutexGuard<'a, M, T>
220where
221 M: RawMutex,
222 T: ?Sized,
223{
224 /// Returns a locked view over a portion of the locked data.
225 pub fn map<U>(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> {
226 let state = this.state;
227 let value = fun(unsafe { &mut *this.value });
228 // Don't run the `drop` method for MutexGuard. The ownership of the underlying
229 // locked state is being moved to the returned MappedMutexGuard.
230 mem::forget(this);
231 MappedMutexGuard { state, value }
232 }
233}
234
235impl<'a, M, T> Deref for MappedMutexGuard<'a, M, T>
236where
237 M: RawMutex,
238 T: ?Sized,
239{
240 type Target = T;
241 fn deref(&self) -> &Self::Target {
242 // Safety: the MutexGuard represents exclusive access to the contents
243 // of the mutex, so it's OK to get it.
244 unsafe { &*self.value }
245 }
246}
247
248impl<'a, M, T> DerefMut for MappedMutexGuard<'a, M, T>
249where
250 M: RawMutex,
251 T: ?Sized,
252{
253 fn deref_mut(&mut self) -> &mut Self::Target {
254 // Safety: the MutexGuard represents exclusive access to the contents
255 // of the mutex, so it's OK to get it.
256 unsafe { &mut *self.value }
257 }
258}
259
260impl<'a, M, T> Drop for MappedMutexGuard<'a, M, T>
261where
262 M: RawMutex,
263 T: ?Sized,
264{
265 fn drop(&mut self) {
266 self.state.lock(|s| {
267 let mut s = unwrap!(s.try_borrow_mut());
268 s.locked = false;
269 s.waker.wake();
270 })
271 }
272}
273
274unsafe impl<M, T> Send for MappedMutexGuard<'_, M, T>
275where
276 M: RawMutex + Sync,
277 T: Send + ?Sized,
278{
279}
280
281unsafe impl<M, T> Sync for MappedMutexGuard<'_, M, T>
282where
283 M: RawMutex + Sync,
284 T: Sync + ?Sized,
285{
286}
287
288#[cfg(test)]
289mod tests {
290 use crate::blocking_mutex::raw::NoopRawMutex;
291 use crate::mutex::{Mutex, MutexGuard};
292
293 #[futures_test::test]
294 async fn mapped_guard_releases_lock_when_dropped() {
295 let mutex: Mutex<NoopRawMutex, [i32; 2]> = Mutex::new([0, 1]);
296
297 {
298 let guard = mutex.lock().await;
299 assert_eq!(*guard, [0, 1]);
300 let mut mapped = MutexGuard::map(guard, |this| &mut this[1]);
301 assert_eq!(*mapped, 1);
302 *mapped = 2;
303 }
304
305 {
306 let guard = mutex.lock().await;
307 assert_eq!(*guard, [0, 2]);
308 let mut mapped = MutexGuard::map(guard, |this| &mut this[1]);
309 assert_eq!(*mapped, 2);
310 *mapped = 3;
311 }
312
313 assert_eq!(*mutex.lock().await, [0, 3]);
314 }
315}