use crate::sync::atomic::{AtomicIsize, Ordering::Acquire}; use crate::thread::yield_now; pub struct RwLock { /// The "mode" value indicates how many threads are waiting on this /// Mutex. Possible values are: /// -1: The lock is locked for writing /// 0: The lock is unlocked /// >=1: The lock is locked for reading /// /// This currently spins waiting for the lock to be freed. An /// optimization would be to involve the ticktimer server to /// coordinate unlocks. mode: AtomicIsize, } const RWLOCK_WRITING: isize = -1; const RWLOCK_FREE: isize = 0; unsafe impl Send for RwLock {} unsafe impl Sync for RwLock {} impl RwLock { #[inline] #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] pub const fn new() -> RwLock { RwLock { mode: AtomicIsize::new(RWLOCK_FREE) } } #[inline] pub unsafe fn read(&self) { while !unsafe { self.try_read() } { yield_now(); } } #[inline] pub unsafe fn try_read(&self) -> bool { self.mode .fetch_update( Acquire, Acquire, |v| if v == RWLOCK_WRITING { None } else { Some(v + 1) }, ) .is_ok() } #[inline] pub unsafe fn write(&self) { while !unsafe { self.try_write() } { yield_now(); } } #[inline] pub unsafe fn try_write(&self) -> bool { self.mode.compare_exchange(RWLOCK_FREE, RWLOCK_WRITING, Acquire, Acquire).is_ok() } #[inline] pub unsafe fn read_unlock(&self) { let previous = self.mode.fetch_sub(1, Acquire); assert!(previous != RWLOCK_FREE); assert!(previous != RWLOCK_WRITING); } #[inline] pub unsafe fn write_unlock(&self) { assert_eq!( self.mode.compare_exchange(RWLOCK_WRITING, RWLOCK_FREE, Acquire, Acquire), Ok(RWLOCK_WRITING) ); } }