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}