embassy_sync/blocking_mutex/mod.rs
1//! Blocking mutex.
2//!
3//! This module provides a blocking mutex that can be used to synchronize data.
4pub mod raw;
5
6use core::cell::UnsafeCell;
7
8use self::raw::RawMutex;
9
10/// Blocking mutex (not async)
11///
12/// Provides a blocking mutual exclusion primitive backed by an implementation of [`raw::RawMutex`].
13///
14/// Which implementation you select depends on the context in which you're using the mutex, and you can choose which kind
15/// of interior mutability fits your use case.
16///
17/// Use [`CriticalSectionMutex`] when data can be shared between threads and interrupts.
18///
19/// Use [`NoopMutex`] when data is only shared between tasks running on the same executor.
20///
21/// Use [`ThreadModeMutex`] when data is shared between tasks running on the same executor but you want a global singleton.
22///
23/// In all cases, the blocking mutex is intended to be short lived and not held across await points.
24/// Use the async [`Mutex`](crate::mutex::Mutex) if you need a lock that is held across await points.
25#[derive(Debug)]
26pub struct Mutex<R, T: ?Sized> {
27 // NOTE: `raw` must be FIRST, so when using ThreadModeMutex the "can't drop in non-thread-mode" gets
28 // to run BEFORE dropping `data`.
29 raw: R,
30 data: UnsafeCell<T>,
31}
32
33unsafe impl<R: RawMutex + Send, T: ?Sized + Send> Send for Mutex<R, T> {}
34unsafe impl<R: RawMutex + Sync, T: ?Sized + Send> Sync for Mutex<R, T> {}
35
36impl<R: RawMutex, T> Mutex<R, T> {
37 /// Creates a new mutex in an unlocked state ready for use.
38 #[inline]
39 pub const fn new(val: T) -> Mutex<R, T> {
40 Mutex {
41 raw: R::INIT,
42 data: UnsafeCell::new(val),
43 }
44 }
45
46 /// Creates a critical section and grants temporary access to the protected data.
47 pub fn lock<U>(&self, f: impl FnOnce(&T) -> U) -> U {
48 self.raw.lock(|| {
49 let ptr = self.data.get() as *const T;
50 let inner = unsafe { &*ptr };
51 f(inner)
52 })
53 }
54
55 /// Creates a critical section and grants temporary mutable access to the protected data.
56 ///
57 /// # Safety
58 ///
59 /// This method is marked unsafe because calling this method re-entrantly, i.e. within
60 /// another `lock_mut` or `lock` closure, violates Rust's aliasing rules. Calling this
61 /// method at the same time from different tasks is safe. For a safe alternative with
62 /// mutable access that never causes UB, use a `RefCell` in a `Mutex`.
63 pub unsafe fn lock_mut<U>(&self, f: impl FnOnce(&mut T) -> U) -> U {
64 self.raw.lock(|| {
65 let ptr = self.data.get() as *mut T;
66 // Safety: we have exclusive access to the data, as long as this mutex is not locked re-entrantly
67 let inner = unsafe { &mut *ptr };
68 f(inner)
69 })
70 }
71}
72
73impl<R, T> Mutex<R, T> {
74 /// Creates a new mutex based on a pre-existing raw mutex.
75 ///
76 /// This allows creating a mutex in a constant context on stable Rust.
77 #[inline]
78 pub const fn const_new(raw_mutex: R, val: T) -> Mutex<R, T> {
79 Mutex {
80 raw: raw_mutex,
81 data: UnsafeCell::new(val),
82 }
83 }
84
85 /// Consumes this mutex, returning the underlying data.
86 #[inline]
87 pub fn into_inner(self) -> T {
88 self.data.into_inner()
89 }
90
91 /// Returns a mutable reference to the underlying data.
92 ///
93 /// Since this call borrows the `Mutex` mutably, no actual locking needs to
94 /// take place---the mutable borrow statically guarantees no locks exist.
95 #[inline]
96 pub fn get_mut(&mut self) -> &mut T {
97 unsafe { &mut *self.data.get() }
98 }
99}
100
101/// A mutex that allows borrowing data across executors and interrupts.
102///
103/// # Safety
104///
105/// This mutex is safe to share between different executors and interrupts.
106pub type CriticalSectionMutex<T> = Mutex<raw::CriticalSectionRawMutex, T>;
107
108/// A mutex that allows borrowing data in the context of a single executor.
109///
110/// # Safety
111///
112/// **This Mutex is only safe within a single executor.**
113pub type NoopMutex<T> = Mutex<raw::NoopRawMutex, T>;
114
115impl<T> Mutex<raw::CriticalSectionRawMutex, T> {
116 /// Borrows the data for the duration of the critical section
117 pub fn borrow<'cs>(&'cs self, _cs: critical_section::CriticalSection<'cs>) -> &'cs T {
118 let ptr = self.data.get() as *const T;
119 unsafe { &*ptr }
120 }
121}
122
123impl<T> Mutex<raw::NoopRawMutex, T> {
124 /// Borrows the data
125 #[allow(clippy::should_implement_trait)]
126 pub fn borrow(&self) -> &T {
127 let ptr = self.data.get() as *const T;
128 unsafe { &*ptr }
129 }
130}
131
132// ThreadModeMutex does NOT use the generic mutex from above because it's special:
133// it's Send+Sync even if T: !Send. There's no way to do that without specialization (I think?).
134//
135// There's still a ThreadModeRawMutex for use with the generic Mutex (handy with Channel, for example),
136// but that will require T: Send even though it shouldn't be needed.
137
138#[cfg(any(cortex_m, feature = "std"))]
139pub use thread_mode_mutex::*;
140#[cfg(any(cortex_m, feature = "std"))]
141mod thread_mode_mutex {
142 use super::*;
143
144 /// A "mutex" that only allows borrowing from thread mode.
145 ///
146 /// # Safety
147 ///
148 /// **This Mutex is only safe on single-core systems.**
149 ///
150 /// On multi-core systems, a `ThreadModeMutex` **is not sufficient** to ensure exclusive access.
151 pub struct ThreadModeMutex<T: ?Sized> {
152 inner: UnsafeCell<T>,
153 }
154
155 // NOTE: ThreadModeMutex only allows borrowing from one execution context ever: thread mode.
156 // Therefore it cannot be used to send non-sendable stuff between execution contexts, so it can
157 // be Send+Sync even if T is not Send (unlike CriticalSectionMutex)
158 unsafe impl<T: ?Sized> Sync for ThreadModeMutex<T> {}
159 unsafe impl<T: ?Sized> Send for ThreadModeMutex<T> {}
160
161 impl<T> ThreadModeMutex<T> {
162 /// Creates a new mutex
163 pub const fn new(value: T) -> Self {
164 ThreadModeMutex {
165 inner: UnsafeCell::new(value),
166 }
167 }
168 }
169
170 impl<T: ?Sized> ThreadModeMutex<T> {
171 /// Lock the `ThreadModeMutex`, granting access to the data.
172 ///
173 /// # Panics
174 ///
175 /// This will panic if not currently running in thread mode.
176 pub fn lock<R>(&self, f: impl FnOnce(&T) -> R) -> R {
177 f(self.borrow())
178 }
179
180 /// Borrows the data
181 ///
182 /// # Panics
183 ///
184 /// This will panic if not currently running in thread mode.
185 pub fn borrow(&self) -> &T {
186 assert!(
187 raw::in_thread_mode(),
188 "ThreadModeMutex can only be borrowed from thread mode."
189 );
190 unsafe { &*self.inner.get() }
191 }
192 }
193
194 impl<T: ?Sized> Drop for ThreadModeMutex<T> {
195 fn drop(&mut self) {
196 // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so
197 // `drop` needs the same guarantees as `lock`. `ThreadModeMutex<T>` is Send even if
198 // T isn't, so without this check a user could create a ThreadModeMutex in thread mode,
199 // send it to interrupt context and drop it there, which would "send" a T even if T is not Send.
200 assert!(
201 raw::in_thread_mode(),
202 "ThreadModeMutex can only be dropped from thread mode."
203 );
204
205 // Drop of the inner `T` happens after this.
206 }
207 }
208}