172 lines
4.2 KiB
Rust
172 lines
4.2 KiB
Rust
use core::{
|
|
cell::UnsafeCell,
|
|
marker::{PhantomData, PhantomPinned},
|
|
ops::{Deref, DerefMut},
|
|
panic::{RefUnwindSafe, UnwindSafe},
|
|
};
|
|
|
|
use crate::{
|
|
bindings::{
|
|
rt_cond, rt_mutex, rt_rwlock, rt_rwlock_rdlock, rt_rwlock_unlock, rt_rwlock_wrlock,
|
|
},
|
|
list::list_init,
|
|
sync::semaphore::c_sem_init,
|
|
};
|
|
|
|
pub struct RwLock<T: ?Sized> {
|
|
l: UnsafeCell<rt_rwlock>,
|
|
_pin_marker: PhantomPinned,
|
|
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> {
|
|
pub const fn init(lock: &'static RwLock<T>, t: T) -> RwLock<T> {
|
|
let l = unsafe { &*(lock.l.get() as *const rt_rwlock) };
|
|
RwLock {
|
|
l: UnsafeCell::new(rt_rwlock {
|
|
m: rt_mutex {
|
|
holder: 0,
|
|
wait_list: list_init(&l.m.wait_list),
|
|
list: list_init(&l.m.list),
|
|
},
|
|
rcond: rt_cond {
|
|
sem: c_sem_init(&l.rcond.sem, 0, 0),
|
|
},
|
|
wcond: rt_cond {
|
|
sem: c_sem_init(&l.wcond.sem, 0, 0),
|
|
},
|
|
num_readers: 0,
|
|
num_writers: 0,
|
|
}),
|
|
_pin_marker: PhantomPinned,
|
|
data: UnsafeCell::new(t),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> RwLock<T> {
|
|
pub fn read(&self) -> RwLockReadGuard<'_, T> {
|
|
unsafe {
|
|
rt_rwlock_rdlock(self.l.get());
|
|
}
|
|
RwLockReadGuard::new(self)
|
|
}
|
|
|
|
pub fn write(&self) -> RwLockWriteGuard<'_, T> {
|
|
unsafe {
|
|
rt_rwlock_wrlock(self.l.get());
|
|
}
|
|
RwLockWriteGuard::new(self)
|
|
}
|
|
|
|
// Probably not useful because this RwLock must be static.
|
|
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> {
|
|
fn new(lock: &RwLock<T>) -> RwLockReadGuard<T> {
|
|
RwLockReadGuard {
|
|
lock,
|
|
_unsend_marker: PhantomData,
|
|
}
|
|
}
|
|
|
|
// Use `unlock` only if you need to explicitly release the RwLock associated with the
|
|
// RwLockReadGuard before it would go out of scope and unlock itself.
|
|
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;
|
|
|
|
fn deref(&self) -> &T {
|
|
unsafe { &*self.lock.data.get() }
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> Drop for RwLockReadGuard<'_, T> {
|
|
#[inline]
|
|
fn drop(&mut self) {
|
|
unsafe { rt_rwlock_unlock(self.lock.l.get()) }
|
|
}
|
|
}
|
|
|
|
pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
|
|
lock: &'a RwLock<T>,
|
|
_unsend_marker: PhantomData<*const ()>,
|
|
}
|
|
|
|
impl<T: ?Sized> RwLockWriteGuard<'_, T> {
|
|
fn new(lock: &RwLock<T>) -> RwLockWriteGuard<T> {
|
|
RwLockWriteGuard {
|
|
lock,
|
|
_unsend_marker: PhantomData,
|
|
}
|
|
}
|
|
|
|
// Use `unlock` only if you need to explicitly release the RwLock associated with the
|
|
// RwLockWriteGuard before it would go out of scope and unlock itself.
|
|
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;
|
|
|
|
fn deref(&self) -> &T {
|
|
unsafe { &*self.lock.data.get() }
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
|
|
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_unlock(self.lock.l.get()) }
|
|
}
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! rwlock {
|
|
($name: ident, $type: ty, $data: expr) => {
|
|
static $name: $crate::sync::RwLock<$type> = $crate::sync::RwLock::init(&$name, $data);
|
|
};
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::test_init;
|
|
|
|
#[test]
|
|
fn fast_path() {
|
|
test_init();
|
|
rwlock!(LOCK, i32, 0);
|
|
*LOCK.write() += 1;
|
|
assert!(*LOCK.read() == 1);
|
|
}
|
|
}
|