rt/rust/src/sync/mutex.rs

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);
}
}