use crate::sync::Static;
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
use core::ptr;
use core::sync::atomic::{compiler_fence, Ordering};
#[inline(never)]
fn already_locked() -> ! {
panic!("IRQ and main thread are attempting to access the same Mutex!")
}
pub struct RawMutex(Static<bool>);
impl RawMutex {
#[must_use]
pub const fn new() -> Self {
RawMutex(Static::new(false))
}
fn raw_lock(&self) -> bool {
if self.0.replace(true) {
false
} else {
compiler_fence(Ordering::Acquire);
true
}
}
fn raw_unlock(&self) {
compiler_fence(Ordering::Release);
if !self.0.replace(false) {
panic!("Internal error: Attempt to unlock a `RawMutex` which is not locked.")
}
}
pub fn lock(&self) -> RawMutexGuard<'_> {
self.try_lock().unwrap_or_else(|| already_locked())
}
pub fn try_lock(&self) -> Option<RawMutexGuard<'_>> {
if self.raw_lock() {
Some(RawMutexGuard(self))
} else {
None
}
}
}
unsafe impl Send for RawMutex {}
unsafe impl Sync for RawMutex {}
pub struct RawMutexGuard<'a>(&'a RawMutex);
impl<'a> Drop for RawMutexGuard<'a> {
fn drop(&mut self) {
self.0.raw_unlock();
}
}
pub struct Mutex<T> {
raw: RawMutex,
data: UnsafeCell<T>,
}
impl<T> Mutex<T> {
#[must_use]
pub const fn new(t: T) -> Self {
Mutex {
raw: RawMutex::new(),
data: UnsafeCell::new(t),
}
}
pub fn lock(&self) -> MutexGuard<'_, T> {
self.try_lock().unwrap_or_else(|| already_locked())
}
pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
if self.raw.raw_lock() {
Some(MutexGuard {
underlying: self,
ptr: self.data.get(),
})
} else {
None
}
}
}
unsafe impl<T> Send for Mutex<T> {}
unsafe impl<T> Sync for Mutex<T> {}
pub struct MutexGuard<'a, T> {
underlying: &'a Mutex<T>,
ptr: *mut T,
}
impl<'a, T> Drop for MutexGuard<'a, T> {
fn drop(&mut self) {
self.underlying.raw.raw_unlock();
}
}
impl<'a, T> Deref for MutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.ptr }
}
}
impl<'a, T> DerefMut for MutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.ptr }
}
}
enum Void {}
pub struct InitOnce<T> {
is_initialized: Static<bool>,
value: UnsafeCell<MaybeUninit<T>>,
}
impl<T> InitOnce<T> {
#[must_use]
pub const fn new() -> Self {
InitOnce {
is_initialized: Static::new(false),
value: UnsafeCell::new(MaybeUninit::uninit()),
}
}
pub fn get(&self, initializer: impl FnOnce() -> T) -> &T {
match self.try_get(|| -> Result<T, Void> { Ok(initializer()) }) {
Ok(v) => v,
_ => unimplemented!(),
}
}
pub fn try_get<E>(&self, initializer: impl FnOnce() -> Result<T, E>) -> Result<&T, E> {
unsafe {
if !self.is_initialized.read() {
crate::interrupt::free(|_| -> Result<(), E> {
if !self.is_initialized.read() {
ptr::write_volatile((*self.value.get()).as_mut_ptr(), initializer()?);
self.is_initialized.write(true);
}
Ok(())
})?;
}
compiler_fence(Ordering::Acquire);
Ok(&*(*self.value.get()).as_mut_ptr())
}
}
}
impl<T> Drop for InitOnce<T> {
fn drop(&mut self) {
if self.is_initialized.read() {
unsafe {
ptr::read((*self.value.get()).as_ptr());
}
}
}
}
unsafe impl<T: Send> Send for InitOnce<T> {}
unsafe impl<T: Sync> Sync for InitOnce<T> {}