use core::cell::UnsafeCell;
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{AtomicBool, Ordering};
use crate::os::irq::NoIrqGuard;
pub struct IrqRawSpinlock {
locked: AtomicBool,
}
impl IrqRawSpinlock {
#[inline]
pub const fn new() -> Self {
Self {
locked: AtomicBool::new(false),
}
}
}
impl Default for IrqRawSpinlock {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl IrqRawSpinlock {
#[inline]
pub fn lock(&self) {
let mut spin_count = 0;
while self
.locked
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
core::hint::spin_loop();
spin_count = (spin_count + 1) & 0xFFF;
if spin_count == 0 {
core::hint::spin_loop();
}
}
}
#[inline]
pub fn try_lock(&self) -> bool {
self.locked
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
}
#[inline]
pub unsafe fn unlock(&self) {
self.locked.store(false, Ordering::Release);
}
#[inline]
pub fn is_locked(&self) -> bool {
self.locked.load(Ordering::Acquire)
}
}
pub struct IrqSpinlock<T> {
raw: IrqRawSpinlock,
data: UnsafeCell<T>,
}
unsafe impl<T: Send> Send for IrqSpinlock<T> {}
unsafe impl<T: Send> Sync for IrqSpinlock<T> {}
pub struct IrqMutexGuard<'a, T> {
lock: &'a IrqSpinlock<T>,
_irq_guard: NoIrqGuard, }
impl<T> IrqSpinlock<T> {
#[inline]
pub const fn new(data: T) -> Self {
Self {
raw: IrqRawSpinlock::new(),
data: UnsafeCell::new(data),
}
}
#[inline]
pub const fn empty() -> IrqSpinlock<()> {
IrqSpinlock::new(())
}
#[inline]
pub fn lock(&self) -> IrqMutexGuard<'_, T> {
for _ in 0..100 {
if let Some(guard) = self.try_lock() {
return guard;
}
}
panic!("IrqSpinlock lock timeout");
}
#[inline]
pub fn try_lock(&self) -> Option<IrqMutexGuard<'_, T>> {
let irq_guard = NoIrqGuard::new();
if self.raw.try_lock() {
Some(IrqMutexGuard::new(self, irq_guard))
} else {
None
}
}
#[inline]
pub fn is_locked(&self) -> bool {
self.raw.is_locked()
}
#[inline]
pub unsafe fn get(&self) -> *mut T {
self.data.get()
}
#[inline]
pub unsafe fn get_mut(&mut self) -> &mut T {
unsafe { &mut *self.data.get() }
}
#[inline]
pub fn into_inner(self) -> T {
self.data.into_inner()
}
#[inline]
pub unsafe fn lock_raw(&self) -> &IrqRawSpinlock {
&self.raw
}
}
impl<'a, T> IrqMutexGuard<'a, T> {
fn new(lock: &'a IrqSpinlock<T>, irq_guard: NoIrqGuard) -> Self {
Self {
lock,
_irq_guard: irq_guard,
}
}
}
impl<T> Drop for IrqMutexGuard<'_, T> {
#[inline]
fn drop(&mut self) {
unsafe {
self.lock.raw.unlock();
}
}
}
impl<T> Deref for IrqMutexGuard<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*self.lock.data.get() }
}
}
impl<T> DerefMut for IrqMutexGuard<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.lock.data.get() }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::alloc::string::String;
#[test]
fn test_irq_raw_spinlock_creation() {
let spinlock = IrqRawSpinlock::new();
assert!(!spinlock.is_locked());
}
#[test]
fn test_irq_raw_spinlock_default() {
let spinlock = IrqRawSpinlock::default();
assert!(!spinlock.is_locked());
}
#[test]
fn test_irq_raw_spinlock_try_lock() {
let spinlock = IrqRawSpinlock::new();
assert!(spinlock.try_lock());
assert!(spinlock.is_locked());
assert!(!spinlock.try_lock());
unsafe {
spinlock.unlock();
}
assert!(!spinlock.is_locked());
assert!(spinlock.try_lock());
unsafe {
spinlock.unlock();
}
}
#[test]
fn test_irq_spinlock_creation() {
let lock = IrqSpinlock::new(42);
assert!(!lock.is_locked());
let value = lock.into_inner();
assert_eq!(value, 42);
}
#[test]
fn test_irq_spinlock_empty() {
let lock: IrqSpinlock<()> = IrqSpinlock::<()>::empty();
assert!(!lock.is_locked());
let value: () = lock.into_inner();
assert_eq!(value, ());
}
#[test]
fn test_irq_spinlock_try_lock() {
let lock = IrqSpinlock::new(42);
{
let guard = lock.try_lock().unwrap();
assert_eq!(*guard, 42);
assert!(lock.is_locked());
}
assert!(!lock.is_locked());
{
let guard = lock.try_lock().unwrap();
assert_eq!(*guard, 42);
}
}
#[test]
fn test_irq_spinlock_try_lock_failure() {
let lock = IrqSpinlock::new(42);
let guard1 = lock.try_lock().unwrap();
assert!(lock.is_locked());
let guard2 = lock.try_lock();
assert!(guard2.is_none());
drop(guard1);
assert!(!lock.is_locked());
}
#[test]
fn test_irq_spinlock_lock_and_modify() {
let lock = IrqSpinlock::new(0);
{
let mut guard = lock.lock();
*guard = 100;
assert_eq!(*guard, 100);
}
assert!(!lock.is_locked());
let guard = lock.lock();
assert_eq!(*guard, 100);
}
#[test]
fn test_multiple_types() {
let string_lock = IrqSpinlock::new(String::from("hello"));
{
let mut guard = string_lock.lock();
guard.push_str(" world");
assert_eq!(guard.as_str(), "hello world");
}
let array_lock = IrqSpinlock::new([1, 2, 3]);
{
let guard = array_lock.lock();
assert_eq!(*guard, [1, 2, 3]);
}
let struct_lock = IrqSpinlock::new((42, "test"));
{
let guard = struct_lock.lock();
assert_eq!(guard.0, 42);
assert_eq!(guard.1, "test");
}
}
}