| 1 | #![cfg_attr (not(feature = "std" ), no_std)] |
| 2 | #![doc = include_str!("../README.md" )] |
| 3 | |
| 4 | mod mutex; |
| 5 | #[cfg (feature = "std" )] |
| 6 | mod std; |
| 7 | |
| 8 | use core::marker::PhantomData; |
| 9 | |
| 10 | pub use self::mutex::Mutex; |
| 11 | |
| 12 | /// Critical section token. |
| 13 | /// |
| 14 | /// An instance of this type indicates that the current thread is executing code within a critical |
| 15 | /// section. |
| 16 | #[derive (Clone, Copy, Debug)] |
| 17 | pub struct CriticalSection<'cs> { |
| 18 | _private: PhantomData<&'cs ()>, |
| 19 | |
| 20 | // Prevent CriticalSection from being Send or Sync |
| 21 | // https://github.com/rust-embedded/critical-section/issues/55 |
| 22 | _not_send_sync: PhantomData<*mut ()>, |
| 23 | } |
| 24 | |
| 25 | impl<'cs> CriticalSection<'cs> { |
| 26 | /// Creates a critical section token. |
| 27 | /// |
| 28 | /// This method is meant to be used to create safe abstractions rather than being directly used |
| 29 | /// in applications. |
| 30 | /// |
| 31 | /// # Safety |
| 32 | /// |
| 33 | /// This must only be called when the current thread is in a critical section. The caller must |
| 34 | /// ensure that the returned instance will not live beyond the end of the critical section. |
| 35 | /// |
| 36 | /// The caller must use adequate fences to prevent the compiler from moving the |
| 37 | /// instructions inside the critical section to the outside of it. Sequentially consistent fences are |
| 38 | /// suggested immediately after entry and immediately before exit from the critical section. |
| 39 | /// |
| 40 | /// Note that the lifetime `'cs` of the returned instance is unconstrained. User code must not |
| 41 | /// be able to influence the lifetime picked for this type, since that might cause it to be |
| 42 | /// inferred to `'static`. |
| 43 | #[inline (always)] |
| 44 | pub unsafe fn new() -> Self { |
| 45 | CriticalSection { |
| 46 | _private: PhantomData, |
| 47 | _not_send_sync: PhantomData, |
| 48 | } |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | #[cfg (any( |
| 53 | all(feature = "restore-state-none" , feature = "restore-state-bool" ), |
| 54 | all(feature = "restore-state-none" , feature = "restore-state-u8" ), |
| 55 | all(feature = "restore-state-none" , feature = "restore-state-u16" ), |
| 56 | all(feature = "restore-state-none" , feature = "restore-state-u32" ), |
| 57 | all(feature = "restore-state-none" , feature = "restore-state-u64" ), |
| 58 | all(feature = "restore-state-bool" , feature = "restore-state-u8" ), |
| 59 | all(feature = "restore-state-bool" , feature = "restore-state-u16" ), |
| 60 | all(feature = "restore-state-bool" , feature = "restore-state-u32" ), |
| 61 | all(feature = "restore-state-bool" , feature = "restore-state-u64" ), |
| 62 | all(feature = "restore-state-bool" , feature = "restore-state-usize" ), |
| 63 | all(feature = "restore-state-u8" , feature = "restore-state-u16" ), |
| 64 | all(feature = "restore-state-u8" , feature = "restore-state-u32" ), |
| 65 | all(feature = "restore-state-u8" , feature = "restore-state-u64" ), |
| 66 | all(feature = "restore-state-u8" , feature = "restore-state-usize" ), |
| 67 | all(feature = "restore-state-u16" , feature = "restore-state-u32" ), |
| 68 | all(feature = "restore-state-u16" , feature = "restore-state-u64" ), |
| 69 | all(feature = "restore-state-u16" , feature = "restore-state-usize" ), |
| 70 | all(feature = "restore-state-u32" , feature = "restore-state-u64" ), |
| 71 | all(feature = "restore-state-u32" , feature = "restore-state-usize" ), |
| 72 | all(feature = "restore-state-u64" , feature = "restore-state-usize" ), |
| 73 | ))] |
| 74 | compile_error!("You must set at most one of these Cargo features: restore-state-none, restore-state-bool, restore-state-u8, restore-state-u16, restore-state-u32, restore-state-u64, restore-state-usize" ); |
| 75 | |
| 76 | #[cfg (not(any( |
| 77 | feature = "restore-state-bool" , |
| 78 | feature = "restore-state-u8" , |
| 79 | feature = "restore-state-u16" , |
| 80 | feature = "restore-state-u32" , |
| 81 | feature = "restore-state-u64" , |
| 82 | feature = "restore-state-usize" |
| 83 | )))] |
| 84 | type RawRestoreStateInner = (); |
| 85 | |
| 86 | #[cfg (feature = "restore-state-bool" )] |
| 87 | type RawRestoreStateInner = bool; |
| 88 | |
| 89 | #[cfg (feature = "restore-state-u8" )] |
| 90 | type RawRestoreStateInner = u8; |
| 91 | |
| 92 | #[cfg (feature = "restore-state-u16" )] |
| 93 | type RawRestoreStateInner = u16; |
| 94 | |
| 95 | #[cfg (feature = "restore-state-u32" )] |
| 96 | type RawRestoreStateInner = u32; |
| 97 | |
| 98 | #[cfg (feature = "restore-state-u64" )] |
| 99 | type RawRestoreStateInner = u64; |
| 100 | |
| 101 | #[cfg (feature = "restore-state-usize" )] |
| 102 | type RawRestoreStateInner = usize; |
| 103 | |
| 104 | // We have RawRestoreStateInner and RawRestoreState so that we don't have to copypaste the docs 5 times. |
| 105 | // In the docs this shows as `pub type RawRestoreState = u8` or whatever the selected type is, because |
| 106 | // the "inner" type alias is private. |
| 107 | |
| 108 | /// Raw, transparent "restore state". |
| 109 | /// |
| 110 | /// This type changes based on which Cargo feature is selected, out of |
| 111 | /// - `restore-state-none` (default, makes the type be `()`) |
| 112 | /// - `restore-state-bool` |
| 113 | /// - `restore-state-u8` |
| 114 | /// - `restore-state-u16` |
| 115 | /// - `restore-state-u32` |
| 116 | /// - `restore-state-u64` |
| 117 | /// - `restore-state-usize` |
| 118 | /// |
| 119 | /// See [`RestoreState`]. |
| 120 | /// |
| 121 | /// User code uses [`RestoreState`] opaquely, critical section implementations |
| 122 | /// use [`RawRestoreState`] so that they can use the inner value. |
| 123 | pub type RawRestoreState = RawRestoreStateInner; |
| 124 | |
| 125 | /// Opaque "restore state". |
| 126 | /// |
| 127 | /// Implementations use this to "carry over" information between acquiring and releasing |
| 128 | /// a critical section. For example, when nesting two critical sections of an |
| 129 | /// implementation that disables interrupts globally, acquiring the inner one won't disable |
| 130 | /// the interrupts since they're already disabled. The impl would use the restore state to "tell" |
| 131 | /// the corresponding release that it does *not* have to reenable interrupts yet, only the |
| 132 | /// outer release should do so. |
| 133 | /// |
| 134 | /// User code uses [`RestoreState`] opaquely, critical section implementations |
| 135 | /// use [`RawRestoreState`] so that they can use the inner value. |
| 136 | #[derive (Clone, Copy, Debug)] |
| 137 | pub struct RestoreState(RawRestoreState); |
| 138 | |
| 139 | impl RestoreState { |
| 140 | /// Create an invalid, dummy `RestoreState`. |
| 141 | /// |
| 142 | /// This can be useful to avoid `Option` when storing a `RestoreState` in a |
| 143 | /// struct field, or a `static`. |
| 144 | /// |
| 145 | /// Note that due to the safety contract of [`acquire`]/[`release`], you must not pass |
| 146 | /// a `RestoreState` obtained from this method to [`release`]. |
| 147 | pub const fn invalid() -> Self { |
| 148 | #[cfg (not(any( |
| 149 | feature = "restore-state-bool" , |
| 150 | feature = "restore-state-u8" , |
| 151 | feature = "restore-state-u16" , |
| 152 | feature = "restore-state-u32" , |
| 153 | feature = "restore-state-u64" , |
| 154 | feature = "restore-state-usize" |
| 155 | )))] |
| 156 | return Self(()); |
| 157 | |
| 158 | #[cfg (feature = "restore-state-bool" )] |
| 159 | return Self(false); |
| 160 | |
| 161 | #[cfg (feature = "restore-state-u8" )] |
| 162 | return Self(0); |
| 163 | |
| 164 | #[cfg (feature = "restore-state-u16" )] |
| 165 | return Self(0); |
| 166 | |
| 167 | #[cfg (feature = "restore-state-u32" )] |
| 168 | return Self(0); |
| 169 | |
| 170 | #[cfg (feature = "restore-state-u64" )] |
| 171 | return Self(0); |
| 172 | |
| 173 | #[cfg (feature = "restore-state-usize" )] |
| 174 | return Self(0); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | /// Acquire a critical section in the current thread. |
| 179 | /// |
| 180 | /// This function is extremely low level. Strongly prefer using [`with`] instead. |
| 181 | /// |
| 182 | /// Nesting critical sections is allowed. The inner critical sections |
| 183 | /// are mostly no-ops since they're already protected by the outer one. |
| 184 | /// |
| 185 | /// # Safety |
| 186 | /// |
| 187 | /// - Each `acquire` call must be paired with exactly one `release` call in the same thread. |
| 188 | /// - `acquire` returns a "restore state" that you must pass to the corresponding `release` call. |
| 189 | /// - `acquire`/`release` pairs must be "properly nested", ie it's not OK to do `a=acquire(); b=acquire(); release(a); release(b);`. |
| 190 | /// - It is UB to call `release` if the critical section is not acquired in the current thread. |
| 191 | /// - It is UB to call `release` with a "restore state" that does not come from the corresponding `acquire` call. |
| 192 | /// - It must provide ordering guarantees at least equivalent to a [`core::sync::atomic::Ordering::Acquire`] |
| 193 | /// on a memory location shared by all critical sections, on which the `release` call will do a |
| 194 | /// [`core::sync::atomic::Ordering::Release`] operation. |
| 195 | #[inline (always)] |
| 196 | pub unsafe fn acquire() -> RestoreState { |
| 197 | unsafeextern "Rust" { |
| 198 | unsafefn _critical_section_1_0_acquire() -> RawRestoreState; |
| 199 | } |
| 200 | |
| 201 | #[allow (clippy::unit_arg)] |
| 202 | RestoreState(_critical_section_1_0_acquire()) |
| 203 | } |
| 204 | |
| 205 | /// Release the critical section. |
| 206 | /// |
| 207 | /// This function is extremely low level. Strongly prefer using [`with`] instead. |
| 208 | /// |
| 209 | /// # Safety |
| 210 | /// |
| 211 | /// See [`acquire`] for the safety contract description. |
| 212 | #[inline (always)] |
| 213 | pub unsafe fn release(restore_state: RestoreState) { |
| 214 | unsafeextern "Rust" { |
| 215 | unsafefn _critical_section_1_0_release(restore_state: RawRestoreState); |
| 216 | } |
| 217 | |
| 218 | #[allow (clippy::unit_arg)] |
| 219 | _critical_section_1_0_release(restore_state.0) |
| 220 | } |
| 221 | |
| 222 | /// Execute closure `f` in a critical section. |
| 223 | /// |
| 224 | /// Nesting critical sections is allowed. The inner critical sections |
| 225 | /// are mostly no-ops since they're already protected by the outer one. |
| 226 | /// |
| 227 | /// # Panics |
| 228 | /// |
| 229 | /// This function panics if the given closure `f` panics. In this case |
| 230 | /// the critical section is released before unwinding. |
| 231 | #[inline ] |
| 232 | pub fn with<R>(f: impl FnOnce(CriticalSection) -> R) -> R { |
| 233 | // Helper for making sure `release` is called even if `f` panics. |
| 234 | struct Guard { |
| 235 | state: RestoreState, |
| 236 | } |
| 237 | |
| 238 | impl Drop for Guard { |
| 239 | #[inline (always)] |
| 240 | fn drop(&mut self) { |
| 241 | unsafe { release(self.state) } |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | let state: RestoreState = unsafe { acquire() }; |
| 246 | let _guard: Guard = Guard { state }; |
| 247 | |
| 248 | unsafe { f(CriticalSection::new()) } |
| 249 | } |
| 250 | |
| 251 | /// Methods required for a critical section implementation. |
| 252 | /// |
| 253 | /// This trait is not intended to be used except when implementing a critical section. |
| 254 | /// |
| 255 | /// # Safety |
| 256 | /// |
| 257 | /// Implementations must uphold the contract specified in [`crate::acquire`] and [`crate::release`]. |
| 258 | pub unsafe trait Impl { |
| 259 | /// Acquire the critical section. |
| 260 | /// |
| 261 | /// # Safety |
| 262 | /// |
| 263 | /// Callers must uphold the contract specified in [`crate::acquire`] and [`crate::release`]. |
| 264 | unsafe fn acquire() -> RawRestoreState; |
| 265 | |
| 266 | /// Release the critical section. |
| 267 | /// |
| 268 | /// # Safety |
| 269 | /// |
| 270 | /// Callers must uphold the contract specified in [`crate::acquire`] and [`crate::release`]. |
| 271 | unsafe fn release(restore_state: RawRestoreState); |
| 272 | } |
| 273 | |
| 274 | /// Set the critical section implementation. |
| 275 | /// |
| 276 | /// # Example |
| 277 | /// |
| 278 | /// ``` |
| 279 | /// # #[cfg (not(feature = "std" ))] // needed for `cargo test --features std` |
| 280 | /// # mod no_std { |
| 281 | /// use critical_section::RawRestoreState; |
| 282 | /// |
| 283 | /// struct MyCriticalSection; |
| 284 | /// critical_section::set_impl!(MyCriticalSection); |
| 285 | /// |
| 286 | /// unsafe impl critical_section::Impl for MyCriticalSection { |
| 287 | /// unsafe fn acquire() -> RawRestoreState { |
| 288 | /// // ... |
| 289 | /// } |
| 290 | /// |
| 291 | /// unsafe fn release(restore_state: RawRestoreState) { |
| 292 | /// // ... |
| 293 | /// } |
| 294 | /// } |
| 295 | /// # } |
| 296 | #[macro_export ] |
| 297 | macro_rules! set_impl { |
| 298 | ($t: ty) => { |
| 299 | #[no_mangle] |
| 300 | unsafe fn _critical_section_1_0_acquire() -> $crate::RawRestoreState { |
| 301 | <$t as $crate::Impl>::acquire() |
| 302 | } |
| 303 | #[no_mangle] |
| 304 | unsafe fn _critical_section_1_0_release(restore_state: $crate::RawRestoreState) { |
| 305 | <$t as $crate::Impl>::release(restore_state) |
| 306 | } |
| 307 | }; |
| 308 | } |
| 309 | |