//! Mutex implementation utilizing an hardware spinlock use core::marker::PhantomData; use core::sync::atomic::Ordering; use embassy_sync::blocking_mutex::raw::RawMutex; use crate::spinlock::{Spinlock, SpinlockValid}; /// A mutex that allows borrowing data across executors and interrupts by utilizing an hardware spinlock /// /// # Safety /// /// This mutex is safe to share between different executors and interrupts. pub struct SpinlockRawMutex { _phantom: PhantomData<()>, } unsafe impl Send for SpinlockRawMutex {} unsafe impl Sync for SpinlockRawMutex {} impl SpinlockRawMutex { /// Create a new `SpinlockRawMutex`. pub const fn new() -> Self { Self { _phantom: PhantomData } } } unsafe impl RawMutex for SpinlockRawMutex where Spinlock: SpinlockValid, { const INIT: Self = Self::new(); fn lock(&self, f: impl FnOnce() -> R) -> R { // Store the initial interrupt state in stack variable let interrupts_active = cortex_m::register::primask::read().is_active(); // Spin until we get the lock loop { // Need to disable interrupts to ensure that we will not deadlock // if an interrupt or higher prio locks the spinlock after we acquire the lock cortex_m::interrupt::disable(); // Ensure the compiler doesn't re-order accesses and violate safety here core::sync::atomic::compiler_fence(Ordering::SeqCst); if let Some(lock) = Spinlock::::try_claim() { // We just acquired the lock. // 1. Forget it, so we don't immediately unlock core::mem::forget(lock); break; } // We didn't get the lock, enable interrupts if they were enabled before we started if interrupts_active { // safety: interrupts are only enabled, if they had been enabled before unsafe { cortex_m::interrupt::enable(); } } } let retval = f(); // Ensure the compiler doesn't re-order accesses and violate safety here core::sync::atomic::compiler_fence(Ordering::SeqCst); // Release the spinlock to allow others to lock mutex again // safety: this point is only reached a spinlock was acquired before unsafe { Spinlock::::release(); } // Re-enable interrupts if they were enabled before the mutex was locked if interrupts_active { // safety: interrupts are only enabled, if they had been enabled before unsafe { cortex_m::interrupt::enable(); } } retval } } pub mod blocking_mutex { //! Mutex implementation utilizing an hardware spinlock use embassy_sync::blocking_mutex::Mutex; use crate::spinlock_mutex::SpinlockRawMutex; /// A mutex that allows borrowing data across executors and interrupts by utilizing an hardware spinlock. /// /// # Safety /// /// This mutex is safe to share between different executors and interrupts. pub type SpinlockMutex = Mutex, T>; }