| #[cfg(test)] | 
 | mod tests; | 
 |  | 
 | use crate::num::NonZeroUsize; | 
 | use crate::sys_common::lazy_box::{LazyBox, LazyInit}; | 
 |  | 
 | use super::waitqueue::{ | 
 |     try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable, | 
 | }; | 
 | use crate::alloc::Layout; | 
 |  | 
 | struct AllocatedRwLock { | 
 |     readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>, | 
 |     writer: SpinMutex<WaitVariable<bool>>, | 
 | } | 
 |  | 
 | pub struct RwLock { | 
 |     inner: LazyBox<AllocatedRwLock>, | 
 | } | 
 |  | 
 | impl LazyInit for AllocatedRwLock { | 
 |     fn init() -> Box<Self> { | 
 |         Box::new(AllocatedRwLock { | 
 |             readers: SpinMutex::new(WaitVariable::new(None)), | 
 |             writer: SpinMutex::new(WaitVariable::new(false)), | 
 |         }) | 
 |     } | 
 | } | 
 |  | 
 | // Check at compile time that RwLock's size and alignment matches the C definition | 
 | // in libunwind (see also `test_c_rwlock_initializer` in `tests`). | 
 | const _: () = { | 
 |     let rust = Layout::new::<RwLock>(); | 
 |     let c = Layout::new::<*mut ()>(); | 
 |     assert!(rust.size() == c.size()); | 
 |     assert!(rust.align() == c.align()); | 
 | }; | 
 |  | 
 | impl RwLock { | 
 |     pub const fn new() -> RwLock { | 
 |         RwLock { inner: LazyBox::new() } | 
 |     } | 
 |  | 
 |     #[inline] | 
 |     pub fn read(&self) { | 
 |         let lock = &*self.inner; | 
 |         let mut rguard = lock.readers.lock(); | 
 |         let wguard = lock.writer.lock(); | 
 |         if *wguard.lock_var() || !wguard.queue_empty() { | 
 |             // Another thread has or is waiting for the write lock, wait | 
 |             drop(wguard); | 
 |             WaitQueue::wait(rguard, || {}); | 
 |         // Another thread has passed the lock to us | 
 |         } else { | 
 |             // No waiting writers, acquire the read lock | 
 |             *rguard.lock_var_mut() = | 
 |                 NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); | 
 |         } | 
 |     } | 
 |  | 
 |     #[inline] | 
 |     pub unsafe fn try_read(&self) -> bool { | 
 |         let lock = &*self.inner; | 
 |         let mut rguard = try_lock_or_false!(lock.readers); | 
 |         let wguard = try_lock_or_false!(lock.writer); | 
 |         if *wguard.lock_var() || !wguard.queue_empty() { | 
 |             // Another thread has or is waiting for the write lock | 
 |             false | 
 |         } else { | 
 |             // No waiting writers, acquire the read lock | 
 |             *rguard.lock_var_mut() = | 
 |                 NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); | 
 |             true | 
 |         } | 
 |     } | 
 |  | 
 |     #[inline] | 
 |     pub fn write(&self) { | 
 |         let lock = &*self.inner; | 
 |         let rguard = lock.readers.lock(); | 
 |         let mut wguard = lock.writer.lock(); | 
 |         if *wguard.lock_var() || rguard.lock_var().is_some() { | 
 |             // Another thread has the lock, wait | 
 |             drop(rguard); | 
 |             WaitQueue::wait(wguard, || {}); | 
 |         // Another thread has passed the lock to us | 
 |         } else { | 
 |             // We are just now obtaining the lock | 
 |             *wguard.lock_var_mut() = true; | 
 |         } | 
 |     } | 
 |  | 
 |     #[inline] | 
 |     pub fn try_write(&self) -> bool { | 
 |         let lock = &*self.inner; | 
 |         let rguard = try_lock_or_false!(lock.readers); | 
 |         let mut wguard = try_lock_or_false!(lock.writer); | 
 |         if *wguard.lock_var() || rguard.lock_var().is_some() { | 
 |             // Another thread has the lock | 
 |             false | 
 |         } else { | 
 |             // We are just now obtaining the lock | 
 |             *wguard.lock_var_mut() = true; | 
 |             true | 
 |         } | 
 |     } | 
 |  | 
 |     #[inline] | 
 |     unsafe fn __read_unlock( | 
 |         &self, | 
 |         mut rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>, | 
 |         wguard: SpinMutexGuard<'_, WaitVariable<bool>>, | 
 |     ) { | 
 |         *rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1); | 
 |         if rguard.lock_var().is_some() { | 
 |             // There are other active readers | 
 |         } else { | 
 |             if let Ok(mut wguard) = WaitQueue::notify_one(wguard) { | 
 |                 // A writer was waiting, pass the lock | 
 |                 *wguard.lock_var_mut() = true; | 
 |                 wguard.drop_after(rguard); | 
 |             } else { | 
 |                 // No writers were waiting, the lock is released | 
 |                 rtassert!(rguard.queue_empty()); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     #[inline] | 
 |     pub unsafe fn read_unlock(&self) { | 
 |         let lock = &*self.inner; | 
 |         let rguard = lock.readers.lock(); | 
 |         let wguard = lock.writer.lock(); | 
 |         unsafe { self.__read_unlock(rguard, wguard) }; | 
 |     } | 
 |  | 
 |     #[inline] | 
 |     unsafe fn __write_unlock( | 
 |         &self, | 
 |         rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>, | 
 |         wguard: SpinMutexGuard<'_, WaitVariable<bool>>, | 
 |     ) { | 
 |         match WaitQueue::notify_one(wguard) { | 
 |             Err(mut wguard) => { | 
 |                 // No writers waiting, release the write lock | 
 |                 *wguard.lock_var_mut() = false; | 
 |                 if let Ok(mut rguard) = WaitQueue::notify_all(rguard) { | 
 |                     // One or more readers were waiting, pass the lock to them | 
 |                     if let NotifiedTcs::All { count } = rguard.notified_tcs() { | 
 |                         *rguard.lock_var_mut() = Some(count) | 
 |                     } else { | 
 |                         unreachable!() // called notify_all | 
 |                     } | 
 |                     rguard.drop_after(wguard); | 
 |                 } else { | 
 |                     // No readers waiting, the lock is released | 
 |                 } | 
 |             } | 
 |             Ok(wguard) => { | 
 |                 // There was a thread waiting for write, just pass the lock | 
 |                 wguard.drop_after(rguard); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     #[inline] | 
 |     pub unsafe fn write_unlock(&self) { | 
 |         let lock = &*self.inner; | 
 |         let rguard = lock.readers.lock(); | 
 |         let wguard = lock.writer.lock(); | 
 |         unsafe { self.__write_unlock(rguard, wguard) }; | 
 |     } | 
 |  | 
 |     // only used by __rust_rwlock_unlock below | 
 |     #[inline] | 
 |     #[cfg_attr(test, allow(dead_code))] | 
 |     unsafe fn unlock(&self) { | 
 |         let lock = &*self.inner; | 
 |         let rguard = lock.readers.lock(); | 
 |         let wguard = lock.writer.lock(); | 
 |         if *wguard.lock_var() == true { | 
 |             unsafe { self.__write_unlock(rguard, wguard) }; | 
 |         } else { | 
 |             unsafe { self.__read_unlock(rguard, wguard) }; | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | // The following functions are needed by libunwind. These symbols are named | 
 | // in pre-link args for the target specification, so keep that in sync. | 
 | #[cfg(not(test))] | 
 | const EINVAL: i32 = 22; | 
 |  | 
 | #[cfg(not(test))] | 
 | #[no_mangle] | 
 | pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RwLock) -> i32 { | 
 |     if p.is_null() { | 
 |         return EINVAL; | 
 |     } | 
 |     unsafe { (*p).read() }; | 
 |     return 0; | 
 | } | 
 |  | 
 | #[cfg(not(test))] | 
 | #[no_mangle] | 
 | pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RwLock) -> i32 { | 
 |     if p.is_null() { | 
 |         return EINVAL; | 
 |     } | 
 |     unsafe { (*p).write() }; | 
 |     return 0; | 
 | } | 
 |  | 
 | #[cfg(not(test))] | 
 | #[no_mangle] | 
 | pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RwLock) -> i32 { | 
 |     if p.is_null() { | 
 |         return EINVAL; | 
 |     } | 
 |     unsafe { (*p).unlock() }; | 
 |     return 0; | 
 | } |