aboutsummaryrefslogtreecommitdiff
path: root/embassy-sync/src
diff options
context:
space:
mode:
authorAlix ANNERAUD <[email protected]>2025-02-28 15:52:07 +0100
committerAlix ANNERAUD <[email protected]>2025-02-28 15:52:07 +0100
commit025d9f6e98086e52cf8025ca475f64899b3d7567 (patch)
treeea89123e7a10ac311f5de2fed75040acb211f657 /embassy-sync/src
parent17301c00e986c5b8536435ea31ebf5aaf13aed17 (diff)
Add RwLock to embassy-sync
Fixes #1394 --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/embassy-rs/embassy/issues/1394?shareId=XXXX-XXXX-XXXX-XXXX).
Diffstat (limited to 'embassy-sync/src')
-rw-r--r--embassy-sync/src/lib.rs1
-rw-r--r--embassy-sync/src/rwlock.rs256
2 files changed, 257 insertions, 0 deletions
diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs
index df0f5e815..5d91b4d9c 100644
--- a/embassy-sync/src/lib.rs
+++ b/embassy-sync/src/lib.rs
@@ -18,6 +18,7 @@ pub mod once_lock;
18pub mod pipe; 18pub mod pipe;
19pub mod priority_channel; 19pub mod priority_channel;
20pub mod pubsub; 20pub mod pubsub;
21pub mod rwlock;
21pub mod semaphore; 22pub mod semaphore;
22pub mod signal; 23pub mod signal;
23pub mod waitqueue; 24pub mod waitqueue;
diff --git a/embassy-sync/src/rwlock.rs b/embassy-sync/src/rwlock.rs
new file mode 100644
index 000000000..15ea8468e
--- /dev/null
+++ b/embassy-sync/src/rwlock.rs
@@ -0,0 +1,256 @@
1use core::cell::RefCell;
2use core::future::{poll_fn, Future};
3use core::ops::{Deref, DerefMut};
4use core::task::Poll;
5
6use crate::blocking_mutex::raw::RawMutex;
7use crate::blocking_mutex::Mutex as BlockingMutex;
8use crate::waitqueue::MultiWakerRegistration;
9
10/// Error returned by [`RwLock::try_read`] and [`RwLock::try_write`]
11#[derive(PartialEq, Eq, Clone, Copy, Debug)]
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13pub struct TryLockError;
14
15/// Async read-write lock.
16///
17/// The lock is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex).
18/// The raw mutex is used to guard access to the internal state. It
19/// is held for very short periods only, while locking and unlocking. It is *not* held
20/// for the entire time the async RwLock is locked.
21///
22/// Which implementation you select depends on the context in which you're using the lock.
23///
24/// Use [`CriticalSectionRawMutex`](crate::blocking_mutex::raw::CriticalSectionRawMutex) when data can be shared between threads and interrupts.
25///
26/// Use [`NoopRawMutex`](crate::blocking_mutex::raw::NoopRawMutex) when data is only shared between tasks running on the same executor.
27///
28/// Use [`ThreadModeRawMutex`](crate::blocking_mutex::raw::ThreadModeRawMutex) when data is shared between tasks running on the same executor but you want a singleton.
29///
30pub struct RwLock<M, T>
31where
32 M: RawMutex,
33 T: ?Sized,
34{
35 state: BlockingMutex<M, RefCell<State>>,
36 inner: RefCell<T>,
37}
38
39struct State {
40 readers: usize,
41 writer: bool,
42 writer_waker: MultiWakerRegistration<1>,
43 reader_wakers: MultiWakerRegistration<8>,
44}
45
46impl State {
47 fn new() -> Self {
48 Self {
49 readers: 0,
50 writer: false,
51 writer_waker: MultiWakerRegistration::new(),
52 reader_wakers: MultiWakerRegistration::new(),
53 }
54 }
55}
56
57impl<M, T> RwLock<M, T>
58where
59 M: RawMutex,
60{
61 /// Create a new read-write lock with the given value.
62 pub const fn new(value: T) -> Self {
63 Self {
64 inner: RefCell::new(value),
65 state: BlockingMutex::new(RefCell::new(State::new())),
66 }
67 }
68}
69
70impl<M, T> RwLock<M, T>
71where
72 M: RawMutex,
73 T: ?Sized,
74{
75 /// Acquire a read lock.
76 ///
77 /// This will wait for the lock to be available if it's already locked for writing.
78 pub fn read(&self) -> impl Future<Output = RwLockReadGuard<'_, M, T>> {
79 poll_fn(|cx| {
80 let mut state = self.state.lock(|s| s.borrow_mut());
81 if state.writer {
82 state.reader_wakers.register(cx.waker());
83 Poll::Pending
84 } else {
85 state.readers += 1;
86 Poll::Ready(RwLockReadGuard { lock: self })
87 }
88 })
89 }
90
91 /// Acquire a write lock.
92 ///
93 /// This will wait for the lock to be available if it's already locked for reading or writing.
94 pub fn write(&self) -> impl Future<Output = RwLockWriteGuard<'_, M, T>> {
95 poll_fn(|cx| {
96 let mut state = self.state.lock(|s| s.borrow_mut());
97 if state.writer || state.readers > 0 {
98 state.writer_waker.register(cx.waker());
99 Poll::Pending
100 } else {
101 state.writer = true;
102 Poll::Ready(RwLockWriteGuard { lock: self })
103 }
104 })
105 }
106
107 /// Attempt to immediately acquire a read lock.
108 ///
109 /// If the lock is already locked for writing, this will return an error instead of waiting.
110 pub fn try_read(&self) -> Result<RwLockReadGuard<'_, M, T>, TryLockError> {
111 let mut state = self.state.lock(|s| s.borrow_mut());
112 if state.writer {
113 Err(TryLockError)
114 } else {
115 state.readers += 1;
116 Ok(RwLockReadGuard { lock: self })
117 }
118 }
119
120 /// Attempt to immediately acquire a write lock.
121 ///
122 /// If the lock is already locked for reading or writing, this will return an error instead of waiting.
123 pub fn try_write(&self) -> Result<RwLockWriteGuard<'_, M, T>, TryLockError> {
124 let mut state = self.state.lock(|s| s.borrow_mut());
125 if state.writer || state.readers > 0 {
126 Err(TryLockError)
127 } else {
128 state.writer = true;
129 Ok(RwLockWriteGuard { lock: self })
130 }
131 }
132
133 /// Consumes this lock, returning the underlying data.
134 pub fn into_inner(self) -> T
135 where
136 T: Sized,
137 {
138 self.inner.into_inner()
139 }
140
141 /// Returns a mutable reference to the underlying data.
142 ///
143 /// Since this call borrows the RwLock mutably, no actual locking needs to
144 /// take place -- the mutable borrow statically guarantees no locks exist.
145 pub fn get_mut(&mut self) -> &mut T {
146 self.inner.get_mut()
147 }
148}
149
150impl<M, T> From<T> for RwLock<M, T>
151where
152 M: RawMutex,
153{
154 fn from(from: T) -> Self {
155 Self::new(from)
156 }
157}
158
159impl<M, T> Default for RwLock<M, T>
160where
161 M: RawMutex,
162 T: Default,
163{
164 fn default() -> Self {
165 Self::new(Default::default())
166 }
167}
168
169/// Async read lock guard.
170///
171/// Owning an instance of this type indicates having
172/// successfully locked the RwLock for reading, and grants access to the contents.
173///
174/// Dropping it unlocks the RwLock.
175#[must_use = "if unused the RwLock will immediately unlock"]
176pub struct RwLockReadGuard<'a, M, T>
177where
178 M: RawMutex,
179 T: ?Sized,
180{
181 lock: &'a RwLock<M, T>,
182}
183
184impl<'a, M, T> Drop for RwLockReadGuard<'a, M, T>
185where
186 M: RawMutex,
187 T: ?Sized,
188{
189 fn drop(&mut self) {
190 let mut state = self.lock.state.lock(|s| s.borrow_mut());
191 state.readers -= 1;
192 if state.readers == 0 {
193 state.writer_waker.wake();
194 }
195 }
196}
197
198impl<'a, M, T> Deref for RwLockReadGuard<'a, M, T>
199where
200 M: RawMutex,
201 T: ?Sized,
202{
203 type Target = T;
204 fn deref(&self) -> &Self::Target {
205 self.lock.inner.borrow()
206 }
207}
208
209/// Async write lock guard.
210///
211/// Owning an instance of this type indicates having
212/// successfully locked the RwLock for writing, and grants access to the contents.
213///
214/// Dropping it unlocks the RwLock.
215#[must_use = "if unused the RwLock will immediately unlock"]
216pub struct RwLockWriteGuard<'a, M, T>
217where
218 M: RawMutex,
219 T: ?Sized,
220{
221 lock: &'a RwLock<M, T>,
222}
223
224impl<'a, M, T> Drop for RwLockWriteGuard<'a, M, T>
225where
226 M: RawMutex,
227 T: ?Sized,
228{
229 fn drop(&mut self) {
230 let mut state = self.lock.state.lock(|s| s.borrow_mut());
231 state.writer = false;
232 state.reader_wakers.wake();
233 state.writer_waker.wake();
234 }
235}
236
237impl<'a, M, T> Deref for RwLockWriteGuard<'a, M, T>
238where
239 M: RawMutex,
240 T: ?Sized,
241{
242 type Target = T;
243 fn deref(&self) -> &Self::Target {
244 self.lock.inner.borrow()
245 }
246}
247
248impl<'a, M, T> DerefMut for RwLockWriteGuard<'a, M, T>
249where
250 M: RawMutex,
251 T: ?Sized,
252{
253 fn deref_mut(&mut self) -> &mut Self::Target {
254 self.lock.inner.borrow_mut()
255 }
256}