use core::{
cell::UnsafeCell,
marker::PhantomData,
ops::{Deref, DerefMut},
panic::{RefUnwindSafe, UnwindSafe},
};
use crate::{
bindings::{
rt_mutex, rt_rwlock, rt_rwlock_rdlock, rt_rwlock_rdunlock, rt_rwlock_timedrdlock,
rt_rwlock_timedwrlock, rt_rwlock_tryrdlock, rt_rwlock_trywrlock, rt_rwlock_wrlock,
rt_rwlock_wrunlock,
},
list::list_init,
ptr_macros::{ptr_to_field, ptr_to_field_mut},
sync::condvar::c_cond_init,
tick::Utick,
};
pub struct RwLock<T: ?Sized> {
rwlock: UnsafeCell<rt_rwlock>,
data: UnsafeCell<T>,
}
unsafe impl<T: ?Sized + Send> Send for RwLock<T> {}
unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
impl<T: ?Sized> UnwindSafe for RwLock<T> {}
impl<T: ?Sized> RefUnwindSafe for RwLock<T> {}
impl<T> RwLock<T> {
#[must_use]
pub const unsafe fn init(this: *const Self, t: T) -> RwLock<T> {
let rwlock = UnsafeCell::raw_get(ptr_to_field!(this, rwlock));
RwLock {
rwlock: UnsafeCell::new(rt_rwlock {
mutex: rt_mutex {
holder: 0,
wait_list: list_init(ptr_to_field_mut!(rwlock, mutex, wait_list)),
list: list_init(ptr_to_field_mut!(rwlock, mutex, list)),
level: -1,
},
cond: c_cond_init(ptr_to_field_mut!(rwlock, cond)),
value: 0,
}),
data: UnsafeCell::new(t),
}
}
}
impl<T: ?Sized> RwLock<T> {
#[inline]
pub fn read(&self) -> RwLockReadGuard<'_, T> {
unsafe { rt_rwlock_rdlock(self.rwlock.get()) }
RwLockReadGuard::new(self)
}
#[inline]
pub fn try_read(&self) -> Option<RwLockReadGuard<'_, T>> {
if unsafe { rt_rwlock_tryrdlock(self.rwlock.get()) } {
Some(RwLockReadGuard::new(self))
} else {
None
}
}
#[inline]
pub fn timed_read(&self, ticks: Utick) -> Option<RwLockReadGuard<'_, T>> {
if unsafe { rt_rwlock_timedrdlock(self.rwlock.get(), ticks) } {
Some(RwLockReadGuard::new(self))
} else {
None
}
}
#[inline]
pub fn write(&self) -> RwLockWriteGuard<'_, T> {
unsafe { rt_rwlock_wrlock(self.rwlock.get()) }
RwLockWriteGuard::new(self)
}
#[inline]
pub fn try_write(&self) -> Option<RwLockWriteGuard<'_, T>> {
if unsafe { rt_rwlock_trywrlock(self.rwlock.get()) } {
Some(RwLockWriteGuard::new(self))
} else {
None
}
}
#[inline]
pub fn timed_write(&self, ticks: Utick) -> Option<RwLockWriteGuard<'_, T>> {
if unsafe { rt_rwlock_timedwrlock(self.rwlock.get(), ticks) } {
Some(RwLockWriteGuard::new(self))
} else {
None
}
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
self.data.get_mut()
}
}
pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
lock: &'a RwLock<T>,
_unsend_marker: PhantomData<*const ()>,
}
impl<T: ?Sized> RwLockReadGuard<'_, T> {
#[inline]
const fn new(lock: &RwLock<T>) -> RwLockReadGuard<'_, T> {
RwLockReadGuard {
lock,
_unsend_marker: PhantomData,
}
}
#[inline]
pub fn unlock(self) {
drop(self)
}
}
unsafe impl<T: ?Sized + Sync> Sync for RwLockReadGuard<'_, T> {}
impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.lock.data.get() }
}
}
impl<T: ?Sized> Drop for RwLockReadGuard<'_, T> {
#[inline]
fn drop(&mut self) {
unsafe { rt_rwlock_rdunlock(self.lock.rwlock.get()) }
}
}
pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
lock: &'a RwLock<T>,
_unsend_marker: PhantomData<*const ()>,
}
impl<T: ?Sized> RwLockWriteGuard<'_, T> {
#[inline]
const fn new(lock: &RwLock<T>) -> RwLockWriteGuard<'_, T> {
RwLockWriteGuard {
lock,
_unsend_marker: PhantomData,
}
}
#[inline]
pub fn unlock(self) {
drop(self)
}
}
unsafe impl<T: ?Sized + Sync> Sync for RwLockWriteGuard<'_, T> {}
impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.lock.data.get() }
}
}
impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.lock.data.get() }
}
}
impl<T: ?Sized> Drop for RwLockWriteGuard<'_, T> {
#[inline]
fn drop(&mut self) {
unsafe { rt_rwlock_wrunlock(self.lock.rwlock.get()) }
}
}
#[macro_export]
macro_rules! rwlock {
($name: ident, $type: ty, $data: expr) => {
static $name: $crate::sync::RwLock<$type> = {
let ptr = &raw const $name;
let data = $data;
unsafe { $crate::sync::RwLock::init(ptr, data) }
};
};
}
#[cfg(test)]
mod tests {
use crate::test_init;
#[test]
fn fast_path() {
test_init();
rwlock!(LOCK, i32, 0);
*LOCK.write() += 1;
assert_eq!(*LOCK.read(), 1);
}
}