#![cfg_attr(not(feature = "std"), no_std)] #![doc = include_str!("../README.md")] mod mutex; #[cfg(feature = "std")] mod std; use core::marker::PhantomData; pub use self::mutex::Mutex; /// Critical section token. /// /// An instance of this type indicates that the current thread is executing code within a critical /// section. #[derive(Clone, Copy, Debug)] pub struct CriticalSection<'cs> { _private: PhantomData<&'cs ()>, // Prevent CriticalSection from being Send or Sync // https://github.com/rust-embedded/critical-section/issues/55 _not_send_sync: PhantomData<*mut ()>, } impl<'cs> CriticalSection<'cs> { /// Creates a critical section token. /// /// This method is meant to be used to create safe abstractions rather than being directly used /// in applications. /// /// # Safety /// /// This must only be called when the current thread is in a critical section. The caller must /// ensure that the returned instance will not live beyond the end of the critical section. /// /// The caller must use adequate fences to prevent the compiler from moving the /// instructions inside the critical section to the outside of it. Sequentially consistent fences are /// suggested immediately after entry and immediately before exit from the critical section. /// /// Note that the lifetime `'cs` of the returned instance is unconstrained. User code must not /// be able to influence the lifetime picked for this type, since that might cause it to be /// inferred to `'static`. #[inline(always)] pub unsafe fn new() -> Self { CriticalSection { _private: PhantomData, _not_send_sync: PhantomData, } } } #[cfg(any( all(feature = "restore-state-none", feature = "restore-state-bool"), all(feature = "restore-state-none", feature = "restore-state-u8"), all(feature = "restore-state-none", feature = "restore-state-u16"), all(feature = "restore-state-none", feature = "restore-state-u32"), all(feature = "restore-state-none", feature = "restore-state-u64"), all(feature = "restore-state-bool", feature = "restore-state-u8"), all(feature = "restore-state-bool", feature = "restore-state-u16"), all(feature = "restore-state-bool", feature = "restore-state-u32"), all(feature = "restore-state-bool", feature = "restore-state-u64"), all(feature = "restore-state-bool", feature = "restore-state-usize"), all(feature = "restore-state-u8", feature = "restore-state-u16"), all(feature = "restore-state-u8", feature = "restore-state-u32"), all(feature = "restore-state-u8", feature = "restore-state-u64"), all(feature = "restore-state-u8", feature = "restore-state-usize"), all(feature = "restore-state-u16", feature = "restore-state-u32"), all(feature = "restore-state-u16", feature = "restore-state-u64"), all(feature = "restore-state-u16", feature = "restore-state-usize"), all(feature = "restore-state-u32", feature = "restore-state-u64"), all(feature = "restore-state-u32", feature = "restore-state-usize"), all(feature = "restore-state-u64", feature = "restore-state-usize"), ))] 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"); #[cfg(not(any( feature = "restore-state-bool", feature = "restore-state-u8", feature = "restore-state-u16", feature = "restore-state-u32", feature = "restore-state-u64", feature = "restore-state-usize" )))] type RawRestoreStateInner = (); #[cfg(feature = "restore-state-bool")] type RawRestoreStateInner = bool; #[cfg(feature = "restore-state-u8")] type RawRestoreStateInner = u8; #[cfg(feature = "restore-state-u16")] type RawRestoreStateInner = u16; #[cfg(feature = "restore-state-u32")] type RawRestoreStateInner = u32; #[cfg(feature = "restore-state-u64")] type RawRestoreStateInner = u64; #[cfg(feature = "restore-state-usize")] type RawRestoreStateInner = usize; // We have RawRestoreStateInner and RawRestoreState so that we don't have to copypaste the docs 5 times. // In the docs this shows as `pub type RawRestoreState = u8` or whatever the selected type is, because // the "inner" type alias is private. /// Raw, transparent "restore state". /// /// This type changes based on which Cargo feature is selected, out of /// - `restore-state-none` (default, makes the type be `()`) /// - `restore-state-bool` /// - `restore-state-u8` /// - `restore-state-u16` /// - `restore-state-u32` /// - `restore-state-u64` /// - `restore-state-usize` /// /// See [`RestoreState`]. /// /// User code uses [`RestoreState`] opaquely, critical section implementations /// use [`RawRestoreState`] so that they can use the inner value. pub type RawRestoreState = RawRestoreStateInner; /// Opaque "restore state". /// /// Implementations use this to "carry over" information between acquiring and releasing /// a critical section. For example, when nesting two critical sections of an /// implementation that disables interrupts globally, acquiring the inner one won't disable /// the interrupts since they're already disabled. The impl would use the restore state to "tell" /// the corresponding release that it does *not* have to reenable interrupts yet, only the /// outer release should do so. /// /// User code uses [`RestoreState`] opaquely, critical section implementations /// use [`RawRestoreState`] so that they can use the inner value. #[derive(Clone, Copy, Debug)] pub struct RestoreState(RawRestoreState); impl RestoreState { /// Create an invalid, dummy `RestoreState`. /// /// This can be useful to avoid `Option` when storing a `RestoreState` in a /// struct field, or a `static`. /// /// Note that due to the safety contract of [`acquire`]/[`release`], you must not pass /// a `RestoreState` obtained from this method to [`release`]. pub const fn invalid() -> Self { #[cfg(not(any( feature = "restore-state-bool", feature = "restore-state-u8", feature = "restore-state-u16", feature = "restore-state-u32", feature = "restore-state-u64", feature = "restore-state-usize" )))] return Self(()); #[cfg(feature = "restore-state-bool")] return Self(false); #[cfg(feature = "restore-state-u8")] return Self(0); #[cfg(feature = "restore-state-u16")] return Self(0); #[cfg(feature = "restore-state-u32")] return Self(0); #[cfg(feature = "restore-state-u64")] return Self(0); #[cfg(feature = "restore-state-usize")] return Self(0); } } /// Acquire a critical section in the current thread. /// /// This function is extremely low level. Strongly prefer using [`with`] instead. /// /// Nesting critical sections is allowed. The inner critical sections /// are mostly no-ops since they're already protected by the outer one. /// /// # Safety /// /// - Each `acquire` call must be paired with exactly one `release` call in the same thread. /// - `acquire` returns a "restore state" that you must pass to the corresponding `release` call. /// - `acquire`/`release` pairs must be "properly nested", ie it's not OK to do `a=acquire(); b=acquire(); release(a); release(b);`. /// - It is UB to call `release` if the critical section is not acquired in the current thread. /// - It is UB to call `release` with a "restore state" that does not come from the corresponding `acquire` call. /// - It must provide ordering guarantees at least equivalent to a [`core::sync::atomic::Ordering::Acquire`] /// on a memory location shared by all critical sections, on which the `release` call will do a /// [`core::sync::atomic::Ordering::Release`] operation. #[inline(always)] pub unsafe fn acquire() -> RestoreState { extern "Rust" { fn _critical_section_1_0_acquire() -> RawRestoreState; } #[allow(clippy::unit_arg)] RestoreState(_critical_section_1_0_acquire()) } /// Release the critical section. /// /// This function is extremely low level. Strongly prefer using [`with`] instead. /// /// # Safety /// /// See [`acquire`] for the safety contract description. #[inline(always)] pub unsafe fn release(restore_state: RestoreState) { extern "Rust" { fn _critical_section_1_0_release(restore_state: RawRestoreState); } #[allow(clippy::unit_arg)] _critical_section_1_0_release(restore_state.0) } /// Execute closure `f` in a critical section. /// /// Nesting critical sections is allowed. The inner critical sections /// are mostly no-ops since they're already protected by the outer one. /// /// # Panics /// /// This function panics if the given closure `f` panics. In this case /// the critical section is released before unwinding. #[inline] pub fn with(f: impl FnOnce(CriticalSection) -> R) -> R { // Helper for making sure `release` is called even if `f` panics. struct Guard { state: RestoreState, } impl Drop for Guard { #[inline(always)] fn drop(&mut self) { unsafe { release(self.state) } } } let state = unsafe { acquire() }; let _guard = Guard { state }; unsafe { f(CriticalSection::new()) } } /// Methods required for a critical section implementation. /// /// This trait is not intended to be used except when implementing a critical section. /// /// # Safety /// /// Implementations must uphold the contract specified in [`crate::acquire`] and [`crate::release`]. pub unsafe trait Impl { /// Acquire the critical section. /// /// # Safety /// /// Callers must uphold the contract specified in [`crate::acquire`] and [`crate::release`]. unsafe fn acquire() -> RawRestoreState; /// Release the critical section. /// /// # Safety /// /// Callers must uphold the contract specified in [`crate::acquire`] and [`crate::release`]. unsafe fn release(restore_state: RawRestoreState); } /// Set the critical section implementation. /// /// # Example /// /// ``` /// # #[cfg(not(feature = "std"))] // needed for `cargo test --features std` /// # mod no_std { /// use critical_section::RawRestoreState; /// /// struct MyCriticalSection; /// critical_section::set_impl!(MyCriticalSection); /// /// unsafe impl critical_section::Impl for MyCriticalSection { /// unsafe fn acquire() -> RawRestoreState { /// // ... /// } /// /// unsafe fn release(restore_state: RawRestoreState) { /// // ... /// } /// } /// # } #[macro_export] macro_rules! set_impl { ($t: ty) => { #[no_mangle] unsafe fn _critical_section_1_0_acquire() -> $crate::RawRestoreState { <$t as $crate::Impl>::acquire() } #[no_mangle] unsafe fn _critical_section_1_0_release(restore_state: $crate::RawRestoreState) { <$t as $crate::Impl>::release(restore_state) } }; }