aboutsummaryrefslogtreecommitdiff
path: root/embassy-sync
diff options
context:
space:
mode:
authorJack Hogan <[email protected]>2024-04-15 14:15:53 -0400
committerGitHub <[email protected]>2024-04-15 14:15:53 -0400
commitaeb4daa22fb767b239781a3f9abd85e70c767917 (patch)
tree4fa9ab1b443e08ad26bc01ef0fd0b11eb492f016 /embassy-sync
parent6ffecb1250bbbdf7f1336479d8f7b7aa1fd00285 (diff)
parent3f4e326ec5ec081742e9f30cb7b0bc936e74009c (diff)
Merge branch 'embassy-rs:main' into main
Diffstat (limited to 'embassy-sync')
-rw-r--r--embassy-sync/CHANGELOG.md5
-rw-r--r--embassy-sync/src/channel.rs27
-rw-r--r--embassy-sync/src/mutex.rs133
3 files changed, 164 insertions, 1 deletions
diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md
index e7db97ef7..3f6b39d8b 100644
--- a/embassy-sync/CHANGELOG.md
+++ b/embassy-sync/CHANGELOG.md
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased
9
10- Add `len`, `is_empty` and `is_full` functions to `Channel`.
11
8## 0.5.0 - 2023-12-04 12## 0.5.0 - 2023-12-04
9 13
10- Add a PriorityChannel. 14- Add a PriorityChannel.
@@ -35,7 +39,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
35- Remove unnecessary uses of `atomic-polyfill` 39- Remove unnecessary uses of `atomic-polyfill`
36- Add `#[must_use]` to all futures. 40- Add `#[must_use]` to all futures.
37 41
38
39## 0.1.0 - 2022-08-26 42## 0.1.0 - 2022-08-26
40 43
41- First release 44- First release
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}