use core::cell::UnsafeCell;
use core::ops::{Deref, DerefMut};
use portable_atomic::{AtomicBool, Ordering};
#[inline(never)]
fn already_locked() -> ! {
panic!("IRQ and main thread are attempting to access the same Lock!")
}
pub struct RawLock(AtomicBool);
impl RawLock {
#[must_use]
pub const fn new() -> Self {
RawLock(AtomicBool::new(false))
}
fn raw_lock(&self) -> bool {
if self.0.swap(true, Ordering::Acquire) {
false
} else {
true
}
}
fn raw_unlock(&self) {
if !self.0.swap(false, Ordering::Release) {
panic!("Internal error: Attempt to unlock a `RawLock` which is not locked.")
}
}
pub fn try_lock(&self) -> Option<RawLockGuard<'_>> {
if self.raw_lock() {
Some(RawLockGuard(self))
} else {
None
}
}
}
unsafe impl Send for RawLock {}
unsafe impl Sync for RawLock {}
pub struct RawLockGuard<'a>(&'a RawLock);
impl Drop for RawLockGuard<'_> {
fn drop(&mut self) {
self.0.raw_unlock();
}
}
pub struct Lock<T> {
raw: RawLock,
data: UnsafeCell<T>,
}
impl<T> Lock<T> {
#[must_use]
pub const fn new(t: T) -> Self {
Lock {
raw: RawLock::new(),
data: UnsafeCell::new(t),
}
}
pub fn lock(&self) -> LockGuard<'_, T> {
self.try_lock().unwrap_or_else(|| already_locked())
}
pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
if self.raw.raw_lock() {
Some(LockGuard {
underlying: self,
ptr: self.data.get(),
})
} else {
None
}
}
}
unsafe impl<T> Send for Lock<T> {}
unsafe impl<T> Sync for Lock<T> {}
pub struct LockGuard<'a, T> {
underlying: &'a Lock<T>,
ptr: *mut T,
}
impl<T> Drop for LockGuard<'_, T> {
fn drop(&mut self) {
self.underlying.raw.raw_unlock();
}
}
impl<T> Deref for LockGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.ptr }
}
}
impl<T> DerefMut for LockGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.ptr }
}
}
#[doc(hidden)]
#[deprecated]
#[allow(dead_code)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __sync_synchronize() {}
#[cfg(test)]
mod tests {
use once_cell::sync::OnceCell;
#[derive(Default)]
#[allow(dead_code)]
struct Storage([u32; 16 / 4]);
#[test_case]
fn check_init_once(_: &mut crate::Gba) {
static CELL: OnceCell<Storage> = OnceCell::new();
core::hint::black_box(CELL.get_or_init(Default::default));
}
#[test_case]
fn check_init_once_many(_: &mut crate::Gba) {
static CELL: OnceCell<Storage> = OnceCell::new();
for _ in 0..1000 {
core::hint::black_box(CELL.get_or_init(Default::default));
}
}
}