diff options
| -rw-r--r-- | embassy-sync/src/mutex.rs | 133 |
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. |
| 4 | use core::cell::{RefCell, UnsafeCell}; | 4 | use core::cell::{RefCell, UnsafeCell}; |
| 5 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| 6 | use core::mem; | ||
| 6 | use core::ops::{Deref, DerefMut}; | 7 | use core::ops::{Deref, DerefMut}; |
| 7 | use core::task::Poll; | 8 | use 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] | ||
| 137 | pub struct MutexGuard<'a, M, T> | 139 | pub struct MutexGuard<'a, M, T> |
| 138 | where | 140 | where |
| 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 | ||
| 147 | impl<'a, M, T> MutexGuard<'a, M, T> | ||
| 148 | where | ||
| 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 | |||
| 145 | impl<'a, M, T> Drop for MutexGuard<'a, M, T> | 166 | impl<'a, M, T> Drop for MutexGuard<'a, M, T> |
| 146 | where | 167 | where |
| 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] | ||
| 210 | pub struct MappedMutexGuard<'a, M, T> | ||
| 211 | where | ||
| 212 | M: RawMutex, | ||
| 213 | T: ?Sized, | ||
| 214 | { | ||
| 215 | state: &'a BlockingMutex<M, RefCell<State>>, | ||
| 216 | value: *mut T, | ||
| 217 | } | ||
| 218 | |||
| 219 | impl<'a, M, T> MappedMutexGuard<'a, M, T> | ||
| 220 | where | ||
| 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 | |||
| 235 | impl<'a, M, T> Deref for MappedMutexGuard<'a, M, T> | ||
| 236 | where | ||
| 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 | |||
| 248 | impl<'a, M, T> DerefMut for MappedMutexGuard<'a, M, T> | ||
| 249 | where | ||
| 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 | |||
| 260 | impl<'a, M, T> Drop for MappedMutexGuard<'a, M, T> | ||
| 261 | where | ||
| 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 | |||
| 274 | unsafe impl<M, T> Send for MappedMutexGuard<'_, M, T> | ||
| 275 | where | ||
| 276 | M: RawMutex + Sync, | ||
| 277 | T: Send + ?Sized, | ||
| 278 | { | ||
| 279 | } | ||
| 280 | |||
| 281 | unsafe impl<M, T> Sync for MappedMutexGuard<'_, M, T> | ||
| 282 | where | ||
| 283 | M: RawMutex + Sync, | ||
| 284 | T: Sync + ?Sized, | ||
| 285 | { | ||
| 286 | } | ||
| 287 | |||
| 288 | #[cfg(test)] | ||
| 289 | mod 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 | } | ||
