aboutsummaryrefslogtreecommitdiff
path: root/embassy-sync/src/mutex.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-08-22 21:46:09 +0200
committerDario Nieuwenhuis <[email protected]>2022-08-22 22:18:13 +0200
commit21072bee48ff6ec19b79e0d9527ad8cc34a4e9e0 (patch)
treeb5b8c0f4b3571989b5fd15152be5639f4334c282 /embassy-sync/src/mutex.rs
parent61356181b223e95f289ca3af3a038a699cde2112 (diff)
split `embassy-util` into `embassy-futures`, `embassy-sync`.
Diffstat (limited to 'embassy-sync/src/mutex.rs')
-rw-r--r--embassy-sync/src/mutex.rs167
1 files changed, 167 insertions, 0 deletions
diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs
new file mode 100644
index 000000000..75a6e8dd3
--- /dev/null
+++ b/embassy-sync/src/mutex.rs
@@ -0,0 +1,167 @@
1//! Async mutex.
2//!
3//! This module provides a mutex that can be used to synchronize data between asynchronous tasks.
4use core::cell::{RefCell, UnsafeCell};
5use core::ops::{Deref, DerefMut};
6use core::task::Poll;
7
8use futures_util::future::poll_fn;
9
10use crate::blocking_mutex::raw::RawMutex;
11use crate::blocking_mutex::Mutex as BlockingMutex;
12use crate::waitqueue::WakerRegistration;
13
14/// Error returned by [`Mutex::try_lock`]
15#[derive(PartialEq, Eq, Clone, Copy, Debug)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub struct TryLockError;
18
19struct State {
20 locked: bool,
21 waker: WakerRegistration,
22}
23
24/// Async mutex.
25///
26/// The mutex is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex).
27/// The raw mutex is used to guard access to the internal "is locked" flag. It
28/// is held for very short periods only, while locking and unlocking. It is *not* held
29/// for the entire time the async Mutex is locked.
30///
31/// Which implementation you select depends on the context in which you're using the mutex.
32///
33/// Use [`CriticalSectionRawMutex`](crate::blocking_mutex::raw::CriticalSectionRawMutex) when data can be shared between threads and interrupts.
34///
35/// Use [`NoopRawMutex`](crate::blocking_mutex::raw::NoopRawMutex) when data is only shared between tasks running on the same executor.
36///
37/// Use [`ThreadModeRawMutex`](crate::blocking_mutex::raw::ThreadModeRawMutex) when data is shared between tasks running on the same executor but you want a singleton.
38///
39pub struct Mutex<M, T>
40where
41 M: RawMutex,
42 T: ?Sized,
43{
44 state: BlockingMutex<M, RefCell<State>>,
45 inner: UnsafeCell<T>,
46}
47
48unsafe impl<M: RawMutex + Send, T: ?Sized + Send> Send for Mutex<M, T> {}
49unsafe impl<M: RawMutex + Sync, T: ?Sized + Send> Sync for Mutex<M, T> {}
50
51/// Async mutex.
52impl<M, T> Mutex<M, T>
53where
54 M: RawMutex,
55{
56 /// Create a new mutex with the given value.
57 pub const fn new(value: T) -> Self {
58 Self {
59 inner: UnsafeCell::new(value),
60 state: BlockingMutex::new(RefCell::new(State {
61 locked: false,
62 waker: WakerRegistration::new(),
63 })),
64 }
65 }
66}
67
68impl<M, T> Mutex<M, T>
69where
70 M: RawMutex,
71 T: ?Sized,
72{
73 /// Lock the mutex.
74 ///
75 /// This will wait for the mutex to be unlocked if it's already locked.
76 pub async fn lock(&self) -> MutexGuard<'_, M, T> {
77 poll_fn(|cx| {
78 let ready = self.state.lock(|s| {
79 let mut s = s.borrow_mut();
80 if s.locked {
81 s.waker.register(cx.waker());
82 false
83 } else {
84 s.locked = true;
85 true
86 }
87 });
88
89 if ready {
90 Poll::Ready(MutexGuard { mutex: self })
91 } else {
92 Poll::Pending
93 }
94 })
95 .await
96 }
97
98 /// Attempt to immediately lock the mutex.
99 ///
100 /// If the mutex is already locked, this will return an error instead of waiting.
101 pub fn try_lock(&self) -> Result<MutexGuard<'_, M, T>, TryLockError> {
102 self.state.lock(|s| {
103 let mut s = s.borrow_mut();
104 if s.locked {
105 Err(TryLockError)
106 } else {
107 s.locked = true;
108 Ok(())
109 }
110 })?;
111
112 Ok(MutexGuard { mutex: self })
113 }
114}
115
116/// Async mutex guard.
117///
118/// Owning an instance of this type indicates having
119/// successfully locked the mutex, and grants access to the contents.
120///
121/// Dropping it unlocks the mutex.
122pub struct MutexGuard<'a, M, T>
123where
124 M: RawMutex,
125 T: ?Sized,
126{
127 mutex: &'a Mutex<M, T>,
128}
129
130impl<'a, M, T> Drop for MutexGuard<'a, M, T>
131where
132 M: RawMutex,
133 T: ?Sized,
134{
135 fn drop(&mut self) {
136 self.mutex.state.lock(|s| {
137 let mut s = s.borrow_mut();
138 s.locked = false;
139 s.waker.wake();
140 })
141 }
142}
143
144impl<'a, M, T> Deref for MutexGuard<'a, M, T>
145where
146 M: RawMutex,
147 T: ?Sized,
148{
149 type Target = T;
150 fn deref(&self) -> &Self::Target {
151 // Safety: the MutexGuard represents exclusive access to the contents
152 // of the mutex, so it's OK to get it.
153 unsafe { &*(self.mutex.inner.get() as *const T) }
154 }
155}
156
157impl<'a, M, T> DerefMut for MutexGuard<'a, M, T>
158where
159 M: RawMutex,
160 T: ?Sized,
161{
162 fn deref_mut(&mut self) -> &mut Self::Target {
163 // Safety: the MutexGuard represents exclusive access to the contents
164 // of the mutex, so it's OK to get it.
165 unsafe { &mut *(self.mutex.inner.get()) }
166 }
167}