148 lines
3.5 KiB
Rust
148 lines
3.5 KiB
Rust
use core::{
|
|
cell::UnsafeCell,
|
|
marker::{PhantomData, PhantomPinned},
|
|
ops::{Deref, DerefMut},
|
|
panic::{RefUnwindSafe, UnwindSafe},
|
|
};
|
|
|
|
use crate::{
|
|
bindings::{rt_mutex, rt_mutex_lock, rt_mutex_timedlock, rt_mutex_trylock, rt_mutex_unlock},
|
|
list::list_init,
|
|
tick::Utick,
|
|
};
|
|
|
|
pub struct Mutex<T: ?Sized> {
|
|
m: UnsafeCell<rt_mutex>,
|
|
_pin_marker: PhantomPinned,
|
|
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> {
|
|
pub const fn init(mutex: &'static Mutex<T>, t: T) -> Mutex<T> {
|
|
let m = unsafe { &*(mutex.m.get() as *const rt_mutex) };
|
|
Mutex {
|
|
m: UnsafeCell::new(rt_mutex {
|
|
holder: 0,
|
|
wait_list: list_init(&m.wait_list),
|
|
list: list_init(&m.list),
|
|
}),
|
|
_pin_marker: PhantomPinned,
|
|
data: UnsafeCell::new(t),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> Mutex<T> {
|
|
pub fn lock(&self) -> MutexGuard<'_, T> {
|
|
unsafe {
|
|
rt_mutex_lock(self.m.get());
|
|
}
|
|
MutexGuard::new(self)
|
|
}
|
|
|
|
pub fn try_lock(&self) -> Option<MutexGuard<T>> {
|
|
if unsafe { rt_mutex_trylock(self.m.get()) } {
|
|
Some(MutexGuard::new(self))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn timed_lock(&self, ticks: Utick) -> Option<MutexGuard<T>> {
|
|
if unsafe { rt_mutex_timedlock(self.m.get(), ticks) } {
|
|
Some(MutexGuard::new(self))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
// Probably not useful because this Mutex must be static.
|
|
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> {
|
|
fn new(lock: &Mutex<T>) -> MutexGuard<T> {
|
|
MutexGuard {
|
|
lock,
|
|
_unsend_marker: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Not supported yet, so using the _unsend_marker instead.
|
|
//impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
|
|
|
|
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
|
|
|
|
impl<T: ?Sized> MutexGuard<'_, T> {
|
|
// Needs to be pub(crate) so that cond can use it.
|
|
pub(crate) fn ptr(&self) -> *mut rt_mutex {
|
|
self.lock.m.get()
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &T {
|
|
unsafe { &*self.lock.data.get() }
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
|
|
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.m.get()) }
|
|
}
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! mutex {
|
|
($name: ident, $type: ty, $data: expr) => {
|
|
static $name: $crate::sync::Mutex<$type> = $crate::sync::Mutex::init(&$name, $data);
|
|
};
|
|
|
|
($name: ident) => {
|
|
rt::mutex!($name, (), ());
|
|
};
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#[test]
|
|
fn fast_path() {
|
|
mutex!(MUTEX, i32, 0);
|
|
*MUTEX.lock() += 1;
|
|
assert!(*MUTEX.lock() == 1);
|
|
}
|
|
|
|
#[test]
|
|
fn try_lock() {
|
|
mutex!(MUTEX, i32, 0);
|
|
{
|
|
let guard = MUTEX.try_lock();
|
|
assert!(MUTEX.try_lock().is_none());
|
|
guard.map(|mut x| *x += 1);
|
|
}
|
|
assert!(*MUTEX.lock() == 1);
|
|
}
|
|
}
|