use core::{
cell::UnsafeCell,
marker::PhantomData,
ops::{Deref, DerefMut},
panic::{RefUnwindSafe, UnwindSafe},
};
use crate::{
bindings::{
RT_MUTEX_LEVEL_NONRECURSIVE, rt_mutex, rt_mutex_lock, rt_mutex_timedlock, rt_mutex_trylock,
rt_mutex_unlock,
},
list::list_init,
ptr_macros::{ptr_to_field, ptr_to_field_mut},
tick::Utick,
};
pub struct Mutex<T: ?Sized> {
mutex: UnsafeCell<rt_mutex>,
data: UnsafeCell<T>,
}
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
impl<T: ?Sized> UnwindSafe for Mutex<T> {}
impl<T: ?Sized> RefUnwindSafe for Mutex<T> {}
impl<T> Mutex<T> {
#[must_use]
pub const unsafe fn init(this: *const Self, t: T) -> Mutex<T> {
let mutex = UnsafeCell::raw_get(ptr_to_field!(this, mutex));
Mutex {
mutex: UnsafeCell::new(rt_mutex {
holder: 0,
wait_list: list_init(ptr_to_field_mut!(mutex, wait_list)),
list: list_init(ptr_to_field_mut!(mutex, list)),
level: RT_MUTEX_LEVEL_NONRECURSIVE,
}),
data: UnsafeCell::new(t),
}
}
}
impl<T: ?Sized> Mutex<T> {
#[inline]
pub fn lock(&self) -> MutexGuard<'_, T> {
unsafe { rt_mutex_lock(self.mutex.get()) }
MutexGuard::new(self)
}
#[inline]
pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
if unsafe { rt_mutex_trylock(self.mutex.get()) } {
Some(MutexGuard::new(self))
} else {
None
}
}
#[inline]
pub fn timed_lock(&self, ticks: Utick) -> Option<MutexGuard<'_, T>> {
if unsafe { rt_mutex_timedlock(self.mutex.get(), ticks) } {
Some(MutexGuard::new(self))
} else {
None
}
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
self.data.get_mut()
}
}
pub struct MutexGuard<'a, T: ?Sized + 'a> {
lock: &'a Mutex<T>,
_unsend_marker: PhantomData<*const ()>,
}
impl<T: ?Sized> MutexGuard<'_, T> {
#[inline]
const fn new(lock: &Mutex<T>) -> MutexGuard<'_, T> {
MutexGuard {
lock,
_unsend_marker: PhantomData,
}
}
}
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
impl<T: ?Sized> MutexGuard<'_, T> {
#[inline]
pub(crate) fn ptr(&self) -> *mut rt_mutex {
self.lock.mutex.get()
}
#[inline]
pub fn unlock(self) {
drop(self)
}
}
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.lock.data.get() }
}
}
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.lock.data.get() }
}
}
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
#[inline]
fn drop(&mut self) {
unsafe { rt_mutex_unlock(self.lock.mutex.get()) }
}
}
#[macro_export]
macro_rules! mutex {
($name: ident, $type: ty, $data: expr) => {
static $name: $crate::sync::Mutex<$type> = {
let ptr = &raw const $name;
let data = $data;
unsafe { $crate::sync::Mutex::init(ptr, data) }
};
};
($name: ident) => {
rt::mutex!($name, (), ());
};
}
#[cfg(test)]
mod tests {
use crate::test_init;
#[test]
fn fast_path() {
test_init();
mutex!(MUTEX, i32, 0);
*MUTEX.lock() += 1;
assert_eq!(*MUTEX.lock(), 1);
}
#[test]
fn try_lock() {
test_init();
mutex!(MUTEX, i32, 0);
if let Some(mut x) = MUTEX.try_lock() {
*x += 1;
}
assert_eq!(*MUTEX.lock(), 1);
}
}