aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlix ANNERAUD <[email protected]>2025-03-17 19:56:17 +0100
committerAlix ANNERAUD <[email protected]>2025-03-17 19:56:17 +0100
commit41276de34fdc30ebb1c73d9aa3f1664cd7a26345 (patch)
tree0fe1f5710c95bf4f3819a4d3be6ac5b8370fc756
parente557ca96065592d5ab6c11c0498a4dee32672635 (diff)
Refactor RwLock implementation to support try_read and try_write methods, enhancing lock acquisition flexibility
-rw-r--r--embassy-sync/src/rwlock.rs270
1 files changed, 253 insertions, 17 deletions
diff --git a/embassy-sync/src/rwlock.rs b/embassy-sync/src/rwlock.rs
index 0e4088c1e..7f0bbec60 100644
--- a/embassy-sync/src/rwlock.rs
+++ b/embassy-sync/src/rwlock.rs
@@ -2,10 +2,10 @@
2//! 2//!
3//! This module provides a read-write lock that can be used to synchronize data between asynchronous tasks. 3//! This module provides a read-write lock that can be used to synchronize data between asynchronous tasks.
4use core::cell::{RefCell, UnsafeCell}; 4use core::cell::{RefCell, UnsafeCell};
5use core::fmt;
6use core::future::{poll_fn, Future}; 5use core::future::{poll_fn, Future};
7use core::ops::{Deref, DerefMut}; 6use core::ops::{Deref, DerefMut};
8use core::task::Poll; 7use core::task::Poll;
8use core::{fmt, mem};
9 9
10use crate::blocking_mutex::raw::RawMutex; 10use crate::blocking_mutex::raw::RawMutex;
11use crate::blocking_mutex::Mutex as BlockingMutex; 11use crate::blocking_mutex::Mutex as BlockingMutex;
@@ -31,11 +31,11 @@ struct State {
31/// 31///
32/// Which implementation you select depends on the context in which you're using the read-write lock. 32/// Which implementation you select depends on the context in which you're using the read-write lock.
33/// 33///
34/// Use [`CriticalSectionMutex`] when data can be shared between threads and interrupts. 34/// Use [`CriticalSectionRawMutex`](crate::blocking_mutex::raw::CriticalSectionRawMutex) when data can be shared between threads and interrupts.
35/// 35///
36/// Use [`NoopMutex`] when data is only shared between tasks running on the same executor. 36/// Use [`NoopRawMutex`](crate::blocking_mutex::raw::NoopRawMutex) when data is only shared between tasks running on the same executor.
37/// 37///
38/// Use [`ThreadModeMutex`] when data is shared between tasks running on the same executor but you want a global singleton. 38/// Use [`ThreadModeRawMutex`](crate::blocking_mutex::raw::ThreadModeRawMutex) when data is shared between tasks running on the same executor but you want a singleton.
39/// 39///
40 40
41pub struct RwLock<M, T> 41pub struct RwLock<M, T>
@@ -51,9 +51,9 @@ unsafe impl<M: RawMutex + Send, T: ?Sized + Send> Send for RwLock<M, T> {}
51unsafe impl<M: RawMutex + Sync, T: ?Sized + Send> Sync for RwLock<M, T> {} 51unsafe impl<M: RawMutex + Sync, T: ?Sized + Send> Sync for RwLock<M, T> {}
52 52
53/// Async read-write lock. 53/// Async read-write lock.
54impl<R, T> RwLock<R, T> 54impl<M, T> RwLock<M, T>
55where 55where
56 R: RawMutex, 56 M: RawMutex,
57{ 57{
58 /// Create a new read-write lock with the given value. 58 /// Create a new read-write lock with the given value.
59 pub const fn new(value: T) -> Self { 59 pub const fn new(value: T) -> Self {
@@ -66,11 +66,17 @@ where
66 })), 66 })),
67 } 67 }
68 } 68 }
69}
69 70
71impl<M, T> RwLock<M, T>
72where
73 M: RawMutex,
74 T: ?Sized,
75{
70 /// Lock the read-write lock for reading. 76 /// Lock the read-write lock for reading.
71 /// 77 ///
72 /// This will wait for the lock to be available if it's already locked for writing. 78 /// This will wait for the lock to be available if it's already locked for writing.
73 pub fn read(&self) -> impl Future<Output = RwLockReadGuard<'_, R, T>> { 79 pub fn read(&self) -> impl Future<Output = RwLockReadGuard<'_, M, T>> {
74 poll_fn(|cx| { 80 poll_fn(|cx| {
75 let ready = self.state.lock(|s| { 81 let ready = self.state.lock(|s| {
76 let mut s = s.borrow_mut(); 82 let mut s = s.borrow_mut();
@@ -94,7 +100,7 @@ where
94 /// Lock the read-write lock for writing. 100 /// Lock the read-write lock for writing.
95 /// 101 ///
96 /// This will wait for the lock to be available if it's already locked for reading or writing. 102 /// This will wait for the lock to be available if it's already locked for reading or writing.
97 pub fn write(&self) -> impl Future<Output = RwLockWriteGuard<'_, R, T>> { 103 pub fn write(&self) -> impl Future<Output = RwLockWriteGuard<'_, M, T>> {
98 poll_fn(|cx| { 104 poll_fn(|cx| {
99 let ready = self.state.lock(|s| { 105 let ready = self.state.lock(|s| {
100 let mut s = s.borrow_mut(); 106 let mut s = s.borrow_mut();
@@ -114,13 +120,43 @@ where
114 } 120 }
115 }) 121 })
116 } 122 }
117}
118 123
119impl<R, T> RwLock<R, T> 124 /// Attempt to immediately lock the rwlock.
120where 125 ///
121 R: RawMutex, 126 /// If the rwlock is already locked, this will return an error instead of waiting.
122 T: ?Sized, 127 pub fn try_read(&self) -> Result<RwLockReadGuard<'_, M, T>, TryLockError> {
123{ 128 self.state
129 .lock(|s| {
130 let mut s = s.borrow_mut();
131 if s.writer {
132 return Err(());
133 }
134 s.readers += 1;
135 Ok(())
136 })
137 .map_err(|_| TryLockError)?;
138
139 Ok(RwLockReadGuard { rwlock: self })
140 }
141
142 /// Attempt to immediately lock the rwlock.
143 ///
144 /// If the rwlock is already locked, this will return an error instead of waiting.
145 pub fn try_write(&self) -> Result<RwLockWriteGuard<'_, M, T>, TryLockError> {
146 self.state
147 .lock(|s| {
148 let mut s = s.borrow_mut();
149 if s.writer || s.readers > 0 {
150 return Err(());
151 }
152 s.writer = true;
153 Ok(())
154 })
155 .map_err(|_| TryLockError)?;
156
157 Ok(RwLockWriteGuard { rwlock: self })
158 }
159
124 /// Consumes this read-write lock, returning the underlying data. 160 /// Consumes this read-write lock, returning the underlying data.
125 pub fn into_inner(self) -> T 161 pub fn into_inner(self) -> T
126 where 162 where
@@ -138,15 +174,15 @@ where
138 } 174 }
139} 175}
140 176
141impl<R: RawMutex, T> From<T> for RwLock<R, T> { 177impl<M: RawMutex, T> From<T> for RwLock<M, T> {
142 fn from(from: T) -> Self { 178 fn from(from: T) -> Self {
143 Self::new(from) 179 Self::new(from)
144 } 180 }
145} 181}
146 182
147impl<R, T> Default for RwLock<R, T> 183impl<M, T> Default for RwLock<M, T>
148where 184where
149 R: RawMutex, 185 M: RawMutex,
150 T: Default, 186 T: Default,
151{ 187{
152 fn default() -> Self { 188 fn default() -> Self {
@@ -154,6 +190,21 @@ where
154 } 190 }
155} 191}
156 192
193impl<M, T> fmt::Debug for RwLock<M, T>
194where
195 M: RawMutex,
196 T: ?Sized + fmt::Debug,
197{
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 let mut d = f.debug_struct("RwLock");
200 match self.try_read() {
201 Ok(guard) => d.field("inner", &&*guard),
202 Err(TryLockError) => d.field("inner", &"Locked"),
203 }
204 .finish_non_exhaustive()
205 }
206}
207
157/// Async read lock guard. 208/// Async read lock guard.
158/// 209///
159/// Owning an instance of this type indicates having 210/// Owning an instance of this type indicates having
@@ -170,6 +221,27 @@ where
170 rwlock: &'a RwLock<R, T>, 221 rwlock: &'a RwLock<R, T>,
171} 222}
172 223
224impl<'a, M, T> RwLockReadGuard<'a, M, T>
225where
226 M: RawMutex,
227 T: ?Sized,
228{
229 /// Map the contents of the `RwLockReadGuard` to a different type.
230 ///
231 /// This is useful for calling methods on the contents of the `RwLockReadGuard` without
232 /// moving out of the guard.
233 pub fn map<U>(this: Self, fun: impl FnOnce(&T) -> &U) -> MappedRwLockReadGuard<'a, M, U> {
234 let rwlock = this.rwlock;
235 let value = fun(unsafe { &mut *this.rwlock.inner.get() });
236
237 mem::forget(this);
238 MappedRwLockReadGuard {
239 state: &rwlock.state,
240 value,
241 }
242 }
243}
244
173impl<'a, M, T> Drop for RwLockReadGuard<'a, M, T> 245impl<'a, M, T> Drop for RwLockReadGuard<'a, M, T>
174where 246where
175 M: RawMutex, 247 M: RawMutex,
@@ -294,6 +366,170 @@ where
294 } 366 }
295} 367}
296 368
369/// A handle to a held `Mutex` that has had a function applied to it via [`MutexGuard::map`] or
370/// [`MappedMutexGuard::map`].
371///
372/// This can be used to hold a subfield of the protected data.
373#[clippy::has_significant_drop]
374pub struct MappedRwLockReadGuard<'a, M, T>
375where
376 M: RawMutex,
377 T: ?Sized,
378{
379 state: &'a BlockingMutex<M, RefCell<State>>,
380 value: *const T,
381}
382
383impl<'a, M, T> Deref for MappedRwLockReadGuard<'a, M, T>
384where
385 M: RawMutex,
386 T: ?Sized,
387{
388 type Target = T;
389 fn deref(&self) -> &Self::Target {
390 // Safety: the MappedRwLockReadGuard represents shared access to the contents
391 // of the read-write lock, so it's OK to get it.
392 unsafe { &*self.value }
393 }
394}
395
396impl<'a, M, T> Drop for MappedRwLockReadGuard<'a, M, T>
397where
398 M: RawMutex,
399 T: ?Sized,
400{
401 fn drop(&mut self) {
402 self.state.lock(|s| {
403 let mut s = unwrap!(s.try_borrow_mut());
404 s.readers -= 1;
405 if s.readers == 0 {
406 s.waker.wake();
407 }
408 })
409 }
410}
411
412unsafe impl<'a, M, T> Send for MappedRwLockReadGuard<'a, M, T>
413where
414 M: RawMutex,
415 T: ?Sized,
416{
417}
418
419unsafe impl<'a, M, T> Sync for MappedRwLockReadGuard<'a, M, T>
420where
421 M: RawMutex,
422 T: ?Sized,
423{
424}
425
426impl<'a, M, T> fmt::Debug for MappedRwLockReadGuard<'a, M, T>
427where
428 M: RawMutex,
429 T: ?Sized + fmt::Debug,
430{
431 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432 fmt::Debug::fmt(&**self, f)
433 }
434}
435
436impl<'a, M, T> fmt::Display for MappedRwLockReadGuard<'a, M, T>
437where
438 M: RawMutex,
439 T: ?Sized + fmt::Display,
440{
441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 fmt::Display::fmt(&**self, f)
443 }
444}
445
446/// A handle to a held `Mutex` that has had a function applied to it via [`MutexGuard::map`] or
447/// [`MappedMutexGuard::map`].
448///
449/// This can be used to hold a subfield of the protected data.
450#[clippy::has_significant_drop]
451pub struct MappedRwLockWriteGuard<'a, M, T>
452where
453 M: RawMutex,
454 T: ?Sized,
455{
456 state: &'a BlockingMutex<M, RefCell<State>>,
457 value: *mut T,
458}
459
460impl<'a, M, T> Deref for MappedRwLockWriteGuard<'a, M, T>
461where
462 M: RawMutex,
463 T: ?Sized,
464{
465 type Target = T;
466 fn deref(&self) -> &Self::Target {
467 // Safety: the MappedRwLockWriteGuard represents exclusive access to the contents
468 // of the read-write lock, so it's OK to get it.
469 unsafe { &*self.value }
470 }
471}
472
473impl<'a, M, T> DerefMut for MappedRwLockWriteGuard<'a, M, T>
474where
475 M: RawMutex,
476 T: ?Sized,
477{
478 fn deref_mut(&mut self) -> &mut Self::Target {
479 // Safety: the MappedRwLockWriteGuard represents exclusive access to the contents
480 // of the read-write lock, so it's OK to get it.
481 unsafe { &mut *self.value }
482 }
483}
484
485impl<'a, M, T> Drop for MappedRwLockWriteGuard<'a, M, T>
486where
487 M: RawMutex,
488 T: ?Sized,
489{
490 fn drop(&mut self) {
491 self.state.lock(|s| {
492 let mut s = unwrap!(s.try_borrow_mut());
493 s.writer = false;
494 s.waker.wake();
495 })
496 }
497}
498
499unsafe impl<'a, M, T> Send for MappedRwLockWriteGuard<'a, M, T>
500where
501 M: RawMutex,
502 T: ?Sized,
503{
504}
505
506unsafe impl<'a, M, T> Sync for MappedRwLockWriteGuard<'a, M, T>
507where
508 M: RawMutex,
509 T: ?Sized,
510{
511}
512
513impl<'a, M, T> fmt::Debug for MappedRwLockWriteGuard<'a, M, T>
514where
515 M: RawMutex,
516 T: ?Sized + fmt::Debug,
517{
518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519 fmt::Debug::fmt(&**self, f)
520 }
521}
522
523impl<'a, M, T> fmt::Display for MappedRwLockWriteGuard<'a, M, T>
524where
525 M: RawMutex,
526 T: ?Sized + fmt::Display,
527{
528 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
529 fmt::Display::fmt(&**self, f)
530 }
531}
532
297#[cfg(test)] 533#[cfg(test)]
298mod tests { 534mod tests {
299 use crate::blocking_mutex::raw::NoopRawMutex; 535 use crate::blocking_mutex::raw::NoopRawMutex;