embassy-rp: Implementation of a SpinlockMutex

This commit is contained in:
Sebastian Quilitz 2025-03-27 16:50:56 +00:00
parent d17d43735f
commit 05a6f7a795
2 changed files with 94 additions and 0 deletions

View File

@ -42,6 +42,7 @@ pub mod rom_data;
pub mod rtc; pub mod rtc;
pub mod spi; pub mod spi;
mod spinlock; mod spinlock;
pub mod spinlock_mutex;
#[cfg(feature = "time-driver")] #[cfg(feature = "time-driver")]
pub mod time_driver; pub mod time_driver;
#[cfg(feature = "_rp235x")] #[cfg(feature = "_rp235x")]

View File

@ -0,0 +1,93 @@
//! 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<const N: usize> {
_phantom: PhantomData<()>,
}
unsafe impl<const N: usize> Send for SpinlockRawMutex<N> {}
unsafe impl<const N: usize> Sync for SpinlockRawMutex<N> {}
impl<const N: usize> SpinlockRawMutex<N> {
/// Create a new `SpinlockRawMutex`.
pub const fn new() -> Self {
Self { _phantom: PhantomData }
}
}
unsafe impl<const N: usize> RawMutex for SpinlockRawMutex<N>
where
Spinlock<N>: SpinlockValid,
{
const INIT: Self = Self::new();
fn lock<R>(&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::<N>::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::<N>::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<const N: usize, T> = Mutex<SpinlockRawMutex<N>, T>;
}