shared_bus/mutex.rs
1use core::cell;
2
3/// Common interface for mutex implementations.
4///
5/// `shared-bus` needs a mutex to ensure only a single device can access the bus at the same time
6/// in concurrent situations. `shared-bus` already implements this trait for a number of existing
7/// mutex types. Most of them are guarded by a feature that needs to be enabled. Here is an
8/// overview:
9///
10/// | Mutex | Feature Name | Notes |
11/// | --- | --- | --- |
12/// | [`shared_bus::NullMutex`][null-mutex] | always available | For sharing within a single execution context. |
13/// | [`std::sync::Mutex`][std-mutex] | `std` | For platforms where `std` is available. |
14/// | [`cortex_m::interrupt::Mutex`][cortexm-mutex] | `cortex-m` | For Cortex-M platforms; Uses a critcal section (i.e. turns off interrupts during bus transactions). |
15///
16/// [null-mutex]: ./struct.NullMutex.html
17/// [std-mutex]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
18/// [cortexm-mutex]: https://docs.rs/cortex-m/0.6.3/cortex_m/interrupt/struct.Mutex.html
19///
20/// For other mutex types, a custom implementation is needed. Due to the orphan rule, it might be
21/// necessary to wrap it in a newtype. As an example, this is what such a custom implementation
22/// might look like:
23///
24/// ```
25/// struct MyMutex<T>(std::sync::Mutex<T>);
26///
27/// impl<T> shared_bus::BusMutex for MyMutex<T> {
28/// type Bus = T;
29///
30/// fn create(v: T) -> Self {
31/// Self(std::sync::Mutex::new(v))
32/// }
33///
34/// fn lock<R, F: FnOnce(&mut Self::Bus) -> R>(&self, f: F) -> R {
35/// let mut v = self.0.lock().unwrap();
36/// f(&mut v)
37/// }
38/// }
39///
40/// // It is also beneficial to define a type alias for the BusManager
41/// type BusManagerCustom<BUS> = shared_bus::BusManager<MyMutex<BUS>>;
42/// ```
43pub trait BusMutex {
44 /// The actual bus that is wrapped inside this mutex.
45 type Bus;
46
47 /// Create a new mutex of this type.
48 fn create(v: Self::Bus) -> Self;
49
50 /// Lock the mutex and give a closure access to the bus inside.
51 fn lock<R, F: FnOnce(&mut Self::Bus) -> R>(&self, f: F) -> R;
52}
53
54/// "Dummy" mutex for sharing in a single task/thread.
55///
56/// This mutex type can be used when all bus users are contained in a single execution context. In
57/// such a situation, no actual mutex is needed, because a RefCell alone is sufficient to ensuring
58/// only a single peripheral can access the bus at the same time.
59///
60/// This mutex type is used with the [`BusManagerSimple`] type.
61///
62/// To uphold safety, this type is `!Send` and `!Sync`.
63///
64/// [`BusManagerSimple`]: ./type.BusManagerSimple.html
65#[derive(Debug)]
66pub struct NullMutex<T> {
67 bus: cell::RefCell<T>,
68}
69
70impl<T> BusMutex for NullMutex<T> {
71 type Bus = T;
72
73 fn create(v: Self::Bus) -> Self {
74 NullMutex {
75 bus: cell::RefCell::new(v),
76 }
77 }
78
79 fn lock<R, F: FnOnce(&mut Self::Bus) -> R>(&self, f: F) -> R {
80 let mut v = self.bus.borrow_mut();
81 f(&mut v)
82 }
83}
84
85#[cfg(feature = "std")]
86impl<T> BusMutex for ::std::sync::Mutex<T> {
87 type Bus = T;
88
89 fn create(v: Self::Bus) -> Self {
90 ::std::sync::Mutex::new(v)
91 }
92
93 fn lock<R, F: FnOnce(&mut Self::Bus) -> R>(&self, f: F) -> R {
94 let mut v = self.lock().unwrap();
95 f(&mut v)
96 }
97}
98
99/// Alias for a Cortex-M mutex.
100///
101/// Based on [`cortex_m::interrupt::Mutex`][cortexm-mutex]. This mutex works by disabling
102/// interrupts while the mutex is locked.
103///
104/// [cortexm-mutex]: https://docs.rs/cortex-m/0.6.3/cortex_m/interrupt/struct.Mutex.html
105///
106/// This type is only available with the `cortex-m` feature.
107#[cfg(feature = "cortex-m")]
108pub type CortexMMutex<T> = cortex_m::interrupt::Mutex<cell::RefCell<T>>;
109
110#[cfg(feature = "cortex-m")]
111impl<T> BusMutex for CortexMMutex<T> {
112 type Bus = T;
113
114 fn create(v: T) -> Self {
115 cortex_m::interrupt::Mutex::new(cell::RefCell::new(v))
116 }
117
118 fn lock<R, F: FnOnce(&mut Self::Bus) -> R>(&self, f: F) -> R {
119 cortex_m::interrupt::free(|cs| {
120 let c = self.borrow(cs);
121 f(&mut c.borrow_mut())
122 })
123 }
124}
125
126/// Wrapper for an interrupt free spin mutex.
127///
128/// Based on [`spin::Mutex`][spin-mutex]. This mutex works by disabling
129/// interrupts while the mutex is locked.
130///
131/// [spin-mutex]: https://docs.rs/spin/0.9.2/spin/type.Mutex.html
132///
133/// This type is only available with the `xtensa` feature.
134#[cfg(feature = "xtensa")]
135pub struct XtensaMutex<T>(spin::Mutex<T>);
136
137#[cfg(feature = "xtensa")]
138impl<T> BusMutex for XtensaMutex<T> {
139 type Bus = T;
140
141 fn create(v: T) -> Self {
142 XtensaMutex(spin::Mutex::new(v))
143 }
144
145 fn lock<R, F: FnOnce(&mut Self::Bus) -> R>(&self, f: F) -> R {
146 xtensa_lx::interrupt::free(|_| f(&mut (*self.0.lock())))
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn std_mutex_api_test() {
156 let t = "hello ".to_string();
157 let m: std::sync::Mutex<_> = BusMutex::create(t);
158
159 BusMutex::lock(&m, |s| {
160 s.push_str("world");
161 });
162
163 BusMutex::lock(&m, |s| {
164 assert_eq!("hello world", s);
165 });
166 }
167}
168
169/// A simple coherency checker for sharing across multiple tasks/threads.
170///
171/// This mutex type can be used when all bus users are contained in a single structure, and is
172/// intended for use with RTIC. When all bus users are contained within a structure managed by RTIC,
173/// RTIC will guarantee that bus collisions do not occur. To protect against accidentaly misuse,
174/// this mutex uses an atomic bool to determine when the bus is in use. If a bus collision is
175/// detected, the code will panic.
176///
177/// This mutex type is used with the [`BusManagerAtomicMutex`] type.
178///
179/// This manager type is explicitly safe to share across threads because it checks to ensure that
180/// collisions due to bus sharing do not occur.
181///
182/// [`BusManagerAtomicMutex`]: ./type.BusManagerAtomicMutex.html
183#[cfg(feature = "cortex-m")]
184#[derive(Debug)]
185pub struct AtomicCheckMutex<BUS> {
186 bus: core::cell::UnsafeCell<BUS>,
187 busy: atomic_polyfill::AtomicBool,
188}
189
190// It is explicitly safe to share this across threads because there is a coherency check using an
191// atomic bool comparison.
192#[cfg(feature = "cortex-m")]
193unsafe impl<BUS> Sync for AtomicCheckMutex<BUS> {}
194
195#[cfg(feature = "cortex-m")]
196impl<BUS> BusMutex for AtomicCheckMutex<BUS> {
197 type Bus = BUS;
198
199 fn create(v: BUS) -> Self {
200 Self {
201 bus: core::cell::UnsafeCell::new(v),
202 busy: atomic_polyfill::AtomicBool::from(false),
203 }
204 }
205
206 fn lock<R, F: FnOnce(&mut Self::Bus) -> R>(&self, f: F) -> R {
207 self.busy
208 .compare_exchange(
209 false,
210 true,
211 core::sync::atomic::Ordering::SeqCst,
212 core::sync::atomic::Ordering::SeqCst,
213 )
214 .expect("Bus conflict");
215 let result = f(unsafe { &mut *self.bus.get() });
216
217 self.busy.store(false, core::sync::atomic::Ordering::SeqCst);
218
219 result
220 }
221}