1 use crate::num::NonZeroUsize;
3 use super::waitqueue::{
4 try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable,
9 readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>,
10 writer: SpinMutex<WaitVariable<bool>>,
13 // Below is to check at compile time, that RWLock has size of 128 bytes.
15 unsafe fn rw_lock_size_assert(r: RWLock) {
16 mem::transmute::<RWLock, [u8; 128]>(r);
20 pub const fn new() -> RWLock {
22 readers: SpinMutex::new(WaitVariable::new(None)),
23 writer: SpinMutex::new(WaitVariable::new(false)),
28 pub unsafe fn read(&self) {
29 let mut rguard = self.readers.lock();
30 let wguard = self.writer.lock();
31 if *wguard.lock_var() || !wguard.queue_empty() {
32 // Another thread has or is waiting for the write lock, wait
34 WaitQueue::wait(rguard);
35 // Another thread has passed the lock to us
37 // No waiting writers, acquire the read lock
38 *rguard.lock_var_mut() =
39 NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
44 pub unsafe fn try_read(&self) -> bool {
45 let mut rguard = try_lock_or_false!(self.readers);
46 let wguard = try_lock_or_false!(self.writer);
47 if *wguard.lock_var() || !wguard.queue_empty() {
48 // Another thread has or is waiting for the write lock
51 // No waiting writers, acquire the read lock
52 *rguard.lock_var_mut() =
53 NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
59 pub unsafe fn write(&self) {
60 let rguard = self.readers.lock();
61 let mut wguard = self.writer.lock();
62 if *wguard.lock_var() || rguard.lock_var().is_some() {
63 // Another thread has the lock, wait
65 WaitQueue::wait(wguard);
66 // Another thread has passed the lock to us
68 // We are just now obtaining the lock
69 *wguard.lock_var_mut() = true;
74 pub unsafe fn try_write(&self) -> bool {
75 let rguard = try_lock_or_false!(self.readers);
76 let mut wguard = try_lock_or_false!(self.writer);
77 if *wguard.lock_var() || rguard.lock_var().is_some() {
78 // Another thread has the lock
81 // We are just now obtaining the lock
82 *wguard.lock_var_mut() = true;
88 unsafe fn __read_unlock(
90 mut rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>,
91 wguard: SpinMutexGuard<'_, WaitVariable<bool>>,
93 *rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1);
94 if rguard.lock_var().is_some() {
95 // There are other active readers
97 if let Ok(mut wguard) = WaitQueue::notify_one(wguard) {
98 // A writer was waiting, pass the lock
99 *wguard.lock_var_mut() = true;
101 // No writers were waiting, the lock is released
102 rtassert!(rguard.queue_empty());
108 pub unsafe fn read_unlock(&self) {
109 let rguard = self.readers.lock();
110 let wguard = self.writer.lock();
111 self.__read_unlock(rguard, wguard);
115 unsafe fn __write_unlock(
117 rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>,
118 wguard: SpinMutexGuard<'_, WaitVariable<bool>>,
120 if let Err(mut wguard) = WaitQueue::notify_one(wguard) {
121 // No writers waiting, release the write lock
122 *wguard.lock_var_mut() = false;
123 if let Ok(mut rguard) = WaitQueue::notify_all(rguard) {
124 // One or more readers were waiting, pass the lock to them
125 if let NotifiedTcs::All { count } = rguard.notified_tcs() {
126 *rguard.lock_var_mut() = Some(count)
128 unreachable!() // called notify_all
131 // No readers waiting, the lock is released
134 // There was a thread waiting for write, just pass the lock
139 pub unsafe fn write_unlock(&self) {
140 let rguard = self.readers.lock();
141 let wguard = self.writer.lock();
142 self.__write_unlock(rguard, wguard);
145 // only used by __rust_rwlock_unlock below
147 #[cfg_attr(test, allow(dead_code))]
148 unsafe fn unlock(&self) {
149 let rguard = self.readers.lock();
150 let wguard = self.writer.lock();
151 if *wguard.lock_var() == true {
152 self.__write_unlock(rguard, wguard);
154 self.__read_unlock(rguard, wguard);
159 pub unsafe fn destroy(&self) {}
162 // The following functions are needed by libunwind. These symbols are named
163 // in pre-link args for the target specification, so keep that in sync.
165 const EINVAL: i32 = 22;
169 pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 {
179 pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 {
188 pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 {
199 use core::array::FixedSizeArray;
200 use crate::mem::{self, MaybeUninit};
202 // Verify that the bytes of initialized RWLock are the same as in
203 // libunwind. If they change, `src/UnwindRustSgx.h` in libunwind needs to
206 fn test_c_rwlock_initializer() {
207 const RWLOCK_INIT: &[u8] = &[
208 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
209 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
210 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
211 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
212 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
213 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
214 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
215 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
216 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
217 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
218 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
219 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
220 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
221 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
222 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
223 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
228 test::black_box(MaybeUninit::<[RWLock; 16]>::zeroed());
232 unsafe fn rwlock_new(init: &mut MaybeUninit<RWLock>) {
233 init.write(RWLock::new());
237 // try hard to make sure that the padding/unused bytes in RWLock
238 // get initialized as 0. If the assertion below fails, that might
239 // just be an issue with the test code and not with the value of
242 let mut init = MaybeUninit::<RWLock>::zeroed();
243 rwlock_new(&mut init);
245 mem::transmute::<_, [u8; 128]>(init.assume_init()).as_slice(),