-
Notifications
You must be signed in to change notification settings - Fork 9
Add multicore example #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c23b337
43c5160
db93b09
e444249
f0b74bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,147 @@ | ||
//! Code that implements the `critical-section` traits on Cortex-R or Cortex-A when only a single | ||
//! core is used. | ||
//! Code that implements the `critical-section` traits on Cortex-R or Cortex-A | ||
//! | ||
//! Only valid if you have a single core. | ||
//! We have single-core and multi-core versions. Select with the | ||
//! `critical-section-single-core` and `critical-section-multi-core` features. | ||
|
||
use core::sync::atomic; | ||
#[cfg(feature = "critical-section-single-core")] | ||
mod single_core { | ||
struct SingleCoreCriticalSection; | ||
|
||
struct SingleCoreCriticalSection; | ||
critical_section::set_impl!(SingleCoreCriticalSection); | ||
critical_section::set_impl!(SingleCoreCriticalSection); | ||
|
||
unsafe impl critical_section::Impl for SingleCoreCriticalSection { | ||
unsafe fn acquire() -> critical_section::RawRestoreState { | ||
// the i bit means "masked" | ||
let was_active = !crate::register::Cpsr::read().i(); | ||
crate::interrupt::disable(); | ||
atomic::compiler_fence(atomic::Ordering::SeqCst); | ||
was_active | ||
/// Indicates the critical section was entered with interrupts on | ||
pub const INT_ON: u8 = 0; | ||
|
||
/// Indicates the critical section was entered with interrupts off | ||
pub const INT_OFF: u8 = 1; | ||
|
||
#[cfg(feature = "critical-section-single-core")] | ||
unsafe impl critical_section::Impl for SingleCoreCriticalSection { | ||
unsafe fn acquire() -> critical_section::RawRestoreState { | ||
use core::sync::atomic; | ||
// the i bit means "masked" | ||
let was_active = !crate::register::Cpsr::read().i(); | ||
crate::interrupt::disable(); | ||
atomic::compiler_fence(atomic::Ordering::SeqCst); | ||
if was_active { | ||
INT_ON | ||
} else { | ||
INT_OFF | ||
} | ||
} | ||
|
||
unsafe fn release(was_active: critical_section::RawRestoreState) { | ||
use core::sync::atomic; | ||
// Only re-enable interrupts if they were enabled before the critical section. | ||
if was_active == INT_ON { | ||
atomic::compiler_fence(atomic::Ordering::SeqCst); | ||
// Safety: This is OK because we're releasing a lock that was | ||
// entered with interrupts enabled | ||
unsafe { | ||
crate::interrupt::enable(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[cfg(feature = "critical-section-multi-core")] | ||
mod multi_core { | ||
struct MultiCoreCriticalSection; | ||
|
||
critical_section::set_impl!(MultiCoreCriticalSection); | ||
|
||
/// The default value for our spin-lock | ||
pub const UNLOCKED: u32 = 0xFFFF_FFFF; | ||
|
||
/// Indicates the critical section was entered with interrupts on, and the spin-lock unlocked | ||
pub const INT_ON_UNLOCKED: u8 = 0; | ||
|
||
/// Indicates the critical section was entered with interrupts off, and the spin-lock locked (by us) | ||
pub const INT_OFF_LOCKED: u8 = 1; | ||
|
||
/// Indicates the critical section was entered with interrupts off, and the spin-lock unlocked | ||
pub const INT_OFF_UNLOCKED: u8 = 2; | ||
|
||
pub static CORE_SPIN_LOCK: core::sync::atomic::AtomicU32 = | ||
core::sync::atomic::AtomicU32::new(UNLOCKED); | ||
unsafe impl critical_section::Impl for MultiCoreCriticalSection { | ||
unsafe fn acquire() -> critical_section::RawRestoreState { | ||
use core::sync::atomic; | ||
|
||
// the i bit means "masked" | ||
let was_active = !crate::register::Cpsr::read().i(); | ||
crate::interrupt::disable(); | ||
|
||
let core_id = crate::asm::core_id(); | ||
|
||
let locked_already = loop { | ||
match CORE_SPIN_LOCK.compare_exchange( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we are on ARM, so maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think spurious failure here would be bad, as we might believe we already hold the lock when it in fact we took the lock. Then we would unlock incorrectly. But I'm happy for someone to argue that it would in fact be correct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you are right. It probably only works when there is a re-try on all error cases. |
||
UNLOCKED, | ||
core_id, | ||
atomic::Ordering::Acquire, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am trying to understand why this does not have to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mara's example at https://marabos.nl/atomics/building-spinlock.html uses As I understand it, we need a load-Acquire when locking the spin-lock, which works with the the store-Release when the spin-lock is unlocked to ensure that the unlock completes before the lock can be taken. Because the lock can only be locked by one thing at a time, there is no subsequent load-Acquire of the spin-lock that we need to ensure happens after the spin-lock is locked. But my grasp on this is tenuous at best. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I'll definitely read that. I read Jons Rust for Rustacean book about that, but its a very tough subject.. |
||
atomic::Ordering::Relaxed, | ||
) { | ||
Ok(_) => { | ||
// we got the lock | ||
break false; | ||
} | ||
Err(n) if n == core_id => { | ||
// we already held the lock | ||
break true; | ||
} | ||
Err(_) => { | ||
// someone else holds the lock | ||
core::hint::spin_loop(); | ||
} | ||
} | ||
}; | ||
|
||
atomic::compiler_fence(atomic::Ordering::SeqCst); | ||
|
||
match (was_active, locked_already) { | ||
(true, true) => { | ||
panic!("Invalid CS state?!"); | ||
} | ||
(true, false) => { | ||
// we need to turn interrupts on, and release the lock | ||
INT_ON_UNLOCKED | ||
} | ||
(false, false) => { | ||
// we need release the lock | ||
INT_OFF_UNLOCKED | ||
} | ||
(false, true) => { | ||
// we need to do nothing | ||
INT_OFF_LOCKED | ||
} | ||
} | ||
} | ||
|
||
unsafe fn release(was_active: critical_section::RawRestoreState) { | ||
use core::sync::atomic; | ||
|
||
unsafe fn release(was_active: critical_section::RawRestoreState) { | ||
// Only re-enable interrupts if they were enabled before the critical section. | ||
if was_active { | ||
atomic::compiler_fence(atomic::Ordering::SeqCst); | ||
// Safety: This is OK because we're releasing a lock that was | ||
// entered with interrupts enabled | ||
unsafe { | ||
crate::interrupt::enable(); | ||
match was_active { | ||
INT_OFF_LOCKED => { | ||
// do nothing | ||
} | ||
INT_OFF_UNLOCKED => { | ||
// the spin-lock was unlocked before, so unlock it | ||
CORE_SPIN_LOCK.store(UNLOCKED, atomic::Ordering::Release); | ||
} | ||
INT_ON_UNLOCKED => { | ||
// the spin-lock was unlocked before, so unlock it | ||
CORE_SPIN_LOCK.store(UNLOCKED, atomic::Ordering::Release); | ||
// Safety: This is OK because we're releasing a lock that was | ||
// entered with interrupts enabled | ||
unsafe { | ||
crate::interrupt::enable(); | ||
} | ||
} | ||
_ => { | ||
unreachable!() | ||
} | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,6 @@ | |
|
||
#![no_std] | ||
|
||
#[cfg(feature = "critical-section-single-core")] | ||
mod critical_section; | ||
|
||
pub mod asm; | ||
|
Uh oh!
There was an error while loading. Please reload this page.