zynq-rs/libcortex_a9/src/mutex.rs

102 lines
2.6 KiB
Rust
Raw Normal View History

2019-11-18 09:13:54 +08:00
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{AtomicU32, Ordering};
2019-11-18 09:13:54 +08:00
use core::cell::UnsafeCell;
use super::asm::*;
/// [Power-saving features](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html)
#[inline]
fn wait_for_update() {
wfe();
}
/// [Power-saving features](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html)
#[inline]
fn signal_update() {
dsb();
sev();
}
const LOCKED: u32 = 1;
const UNLOCKED: u32 = 0;
2019-11-18 09:13:54 +08:00
/// Mutex implementation for Cortex-A9
///
/// [ARM Synchronization Primitives Development Article: Implementing a mutex](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html)
2019-11-18 09:13:54 +08:00
pub struct Mutex<T> {
locked: AtomicU32,
2019-11-18 09:13:54 +08:00
inner: UnsafeCell<T>,
}
unsafe impl<T: Send> Sync for Mutex<T> {}
unsafe impl<T: Send> Send for Mutex<T> {}
impl<T> Mutex<T> {
/// Constructor, const-fn
2019-11-18 09:13:54 +08:00
pub const fn new(inner: T) -> Self {
Mutex{
locked: AtomicU32::new(UNLOCKED),
2019-11-18 09:13:54 +08:00
inner: UnsafeCell::new(inner),
}
}
/// Lock the Mutex, blocks when already locked
2019-11-18 09:13:54 +08:00
pub fn lock(&self) -> MutexGuard<T> {
let mut irq = unsafe { enter_critical() };
while self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED {
unsafe {
exit_critical(irq);
wait_for_update();
irq = enter_critical();
}
}
2019-11-18 09:13:54 +08:00
dmb();
MutexGuard { mutex: self, irq }
2019-11-18 09:13:54 +08:00
}
pub fn try_lock(&self) -> Option<MutexGuard<T>> {
let irq = unsafe { enter_critical() };
if self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED {
unsafe { exit_critical(irq) };
None
} else {
dmb();
Some(MutexGuard { mutex: self, irq })
}
}
2019-11-18 09:13:54 +08:00
fn unlock(&self) {
dmb();
self.locked.store(UNLOCKED, Ordering::Release);
signal_update();
2019-11-18 09:13:54 +08:00
}
}
/// Returned by `Mutex.lock()`, allows access to data via
/// `Deref`/`DerefMutx`
2019-11-18 09:13:54 +08:00
pub struct MutexGuard<'a, T> {
mutex: &'a Mutex<T>,
irq: bool,
2019-11-18 09:13:54 +08:00
}
impl<'a, T> Deref for MutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.mutex.inner.get() }
}
}
impl<'a, T> DerefMut for MutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.mutex.inner.get() }
}
}
/// Automatically `Mutex.unlock()` when this reference is dropped
2019-11-18 09:13:54 +08:00
impl<'a, T> Drop for MutexGuard<'a, T> {
fn drop(&mut self) {
self.mutex.unlock();
unsafe { exit_critical(self.irq) };
2019-11-18 09:13:54 +08:00
}
}