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