aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy/src/lib.rs4
-rw-r--r--embassy/src/mutex.rs167
-rw-r--r--examples/nrf/src/bin/mutex.rs44
3 files changed, 213 insertions, 2 deletions
diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs
index 6b24b5989..ec697b40a 100644
--- a/embassy/src/lib.rs
+++ b/embassy/src/lib.rs
@@ -10,15 +10,15 @@ pub(crate) mod fmt;
10 10
11pub mod blocking_mutex; 11pub mod blocking_mutex;
12pub mod channel; 12pub mod channel;
13pub mod waitqueue;
14
15pub mod executor; 13pub mod executor;
16#[cfg(cortex_m)] 14#[cfg(cortex_m)]
17pub mod interrupt; 15pub mod interrupt;
18pub mod io; 16pub mod io;
17pub mod mutex;
19#[cfg(feature = "time")] 18#[cfg(feature = "time")]
20pub mod time; 19pub mod time;
21pub mod util; 20pub mod util;
21pub mod waitqueue;
22 22
23#[cfg(feature = "nightly")] 23#[cfg(feature = "nightly")]
24pub use embassy_macros::{main, task}; 24pub use embassy_macros::{main, task};
diff --git a/embassy/src/mutex.rs b/embassy/src/mutex.rs
new file mode 100644
index 000000000..27353bd4d
--- /dev/null
+++ b/embassy/src/mutex.rs
@@ -0,0 +1,167 @@
1/// Async mutex.
2///
3/// The mutex is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex).
4/// The raw mutex is used to guard access to the internal "is locked" flag. It
5/// is held for very short periods only, while locking and unlocking. It is *not* held
6/// for the entire time the async Mutex is locked.
7use core::cell::{RefCell, UnsafeCell};
8use core::ops::{Deref, DerefMut};
9use core::task::Poll;
10use futures::future::poll_fn;
11
12use crate::blocking_mutex::raw::RawMutex;
13use crate::blocking_mutex::Mutex as BlockingMutex;
14use crate::waitqueue::WakerRegistration;
15
16/// Error returned by [`Mutex::try_lock`]
17#[derive(PartialEq, Eq, Clone, Copy, Debug)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub struct TryLockError;
20
21struct State {
22 locked: bool,
23 waker: WakerRegistration,
24}
25
26pub struct Mutex<M, T>
27where
28 M: RawMutex,
29 T: ?Sized,
30{
31 state: BlockingMutex<M, RefCell<State>>,
32 inner: UnsafeCell<T>,
33}
34
35unsafe impl<M: RawMutex + Send, T: ?Sized + Send> Send for Mutex<M, T> {}
36unsafe impl<M: RawMutex + Sync, T: ?Sized + Send> Sync for Mutex<M, T> {}
37
38/// Async mutex.
39impl<M, T> Mutex<M, T>
40where
41 M: RawMutex,
42{
43 /// Create a new mutex with the given value.
44 #[cfg(feature = "nightly")]
45 pub const fn new(value: T) -> Self {
46 Self {
47 inner: UnsafeCell::new(value),
48 state: BlockingMutex::new(RefCell::new(State {
49 locked: false,
50 waker: WakerRegistration::new(),
51 })),
52 }
53 }
54
55 /// Create a new mutex with the given value.
56 #[cfg(not(feature = "nightly"))]
57 pub 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}
diff --git a/examples/nrf/src/bin/mutex.rs b/examples/nrf/src/bin/mutex.rs
new file mode 100644
index 000000000..db1b72f6d
--- /dev/null
+++ b/examples/nrf/src/bin/mutex.rs
@@ -0,0 +1,44 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::blocking_mutex::raw::ThreadModeRawMutex;
7use embassy::executor::Spawner;
8use embassy::mutex::Mutex;
9use embassy::time::{Duration, Timer};
10use embassy_nrf::Peripherals;
11
12use defmt_rtt as _; // global logger
13use panic_probe as _;
14
15static MUTEX: Mutex<ThreadModeRawMutex, u32> = Mutex::new(0);
16
17#[embassy::task]
18async fn my_task() {
19 loop {
20 {
21 let mut m = MUTEX.lock().await;
22 info!("start long operation");
23 *m += 1000;
24
25 // Hold the mutex for a long time.
26 Timer::after(Duration::from_secs(1)).await;
27 info!("end long operation: count = {}", *m);
28 }
29
30 Timer::after(Duration::from_secs(1)).await;
31 }
32}
33
34#[embassy::main]
35async fn main(spawner: Spawner, _p: Peripherals) {
36 unwrap!(spawner.spawn(my_task()));
37
38 loop {
39 Timer::after(Duration::from_millis(300)).await;
40 let mut m = MUTEX.lock().await;
41 *m += 1;
42 info!("short operation: count = {}", *m);
43 }
44}