embassy-rp: Implementation of a SpinlockMutex
This commit is contained in:
		
							parent
							
								
									d17d43735f
								
							
						
					
					
						commit
						05a6f7a795
					
				| @ -42,6 +42,7 @@ pub mod rom_data; | ||||
| pub mod rtc; | ||||
| pub mod spi; | ||||
| mod spinlock; | ||||
| pub mod spinlock_mutex; | ||||
| #[cfg(feature = "time-driver")] | ||||
| pub mod time_driver; | ||||
| #[cfg(feature = "_rp235x")] | ||||
|  | ||||
							
								
								
									
										93
									
								
								embassy-rp/src/spinlock_mutex.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								embassy-rp/src/spinlock_mutex.rs
									
									
									
									
									
										Normal 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>; | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user