aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-sync/src/mutex.rs133
1 files changed, 133 insertions, 0 deletions
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}