2 use crate::alloc::{self, Layout};
3 use crate::num::NonZeroUsize;
9 use super::waitqueue::{
10 try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable,
15 readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>,
16 writer: SpinMutex<WaitVariable<bool>>,
19 // Below is to check at compile time, that RWLock has size of 128 bytes.
21 unsafe fn rw_lock_size_assert(r: RWLock) {
22 mem::transmute::<RWLock, [u8; 128]>(r);
26 pub const fn new() -> RWLock {
28 readers: SpinMutex::new(WaitVariable::new(None)),
29 writer: SpinMutex::new(WaitVariable::new(false)),
34 pub unsafe fn read(&self) {
35 let mut rguard = self.readers.lock();
36 let wguard = self.writer.lock();
37 if *wguard.lock_var() || !wguard.queue_empty() {
38 // Another thread has or is waiting for the write lock, wait
40 WaitQueue::wait(rguard);
41 // Another thread has passed the lock to us
43 // No waiting writers, acquire the read lock
44 *rguard.lock_var_mut() =
45 NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
50 pub unsafe fn try_read(&self) -> bool {
51 let mut rguard = try_lock_or_false!(self.readers);
52 let wguard = try_lock_or_false!(self.writer);
53 if *wguard.lock_var() || !wguard.queue_empty() {
54 // Another thread has or is waiting for the write lock
57 // No waiting writers, acquire the read lock
58 *rguard.lock_var_mut() =
59 NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
65 pub unsafe fn write(&self) {
66 let rguard = self.readers.lock();
67 let mut wguard = self.writer.lock();
68 if *wguard.lock_var() || rguard.lock_var().is_some() {
69 // Another thread has the lock, wait
71 WaitQueue::wait(wguard);
72 // Another thread has passed the lock to us
74 // We are just now obtaining the lock
75 *wguard.lock_var_mut() = true;
80 pub unsafe fn try_write(&self) -> bool {
81 let rguard = try_lock_or_false!(self.readers);
82 let mut wguard = try_lock_or_false!(self.writer);
83 if *wguard.lock_var() || rguard.lock_var().is_some() {
84 // Another thread has the lock
87 // We are just now obtaining the lock
88 *wguard.lock_var_mut() = true;
94 unsafe fn __read_unlock(
96 mut rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>,
97 wguard: SpinMutexGuard<WaitVariable<bool>>,
99 *rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1);
100 if rguard.lock_var().is_some() {
101 // There are other active readers
103 if let Ok(mut wguard) = WaitQueue::notify_one(wguard) {
104 // A writer was waiting, pass the lock
105 *wguard.lock_var_mut() = true;
107 // No writers were waiting, the lock is released
108 assert!(rguard.queue_empty());
114 pub unsafe fn read_unlock(&self) {
115 let rguard = self.readers.lock();
116 let wguard = self.writer.lock();
117 self.__read_unlock(rguard, wguard);
121 unsafe fn __write_unlock(
123 rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>,
124 wguard: SpinMutexGuard<WaitVariable<bool>>,
126 if let Err(mut wguard) = WaitQueue::notify_one(wguard) {
127 // No writers waiting, release the write lock
128 *wguard.lock_var_mut() = false;
129 if let Ok(mut rguard) = WaitQueue::notify_all(rguard) {
130 // One or more readers were waiting, pass the lock to them
131 if let NotifiedTcs::All { count } = rguard.notified_tcs() {
132 *rguard.lock_var_mut() = Some(count)
134 unreachable!() // called notify_all
137 // No readers waiting, the lock is released
140 // There was a thread waiting for write, just pass the lock
145 pub unsafe fn write_unlock(&self) {
146 let rguard = self.readers.lock();
147 let wguard = self.writer.lock();
148 self.__write_unlock(rguard, wguard);
151 // only used by __rust_rwlock_unlock below
153 #[cfg_attr(test, allow(dead_code))]
154 unsafe fn unlock(&self) {
155 let rguard = self.readers.lock();
156 let wguard = self.writer.lock();
157 if *wguard.lock_var() == true {
158 self.__write_unlock(rguard, wguard);
160 self.__read_unlock(rguard, wguard);
165 pub unsafe fn destroy(&self) {}
169 const EINVAL: i32 = 22;
171 // used by libunwind port
174 pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 {
184 pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 {
193 pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 {
201 // the following functions are also used by the libunwind port. They're
202 // included here to make sure parallel codegen and LTO don't mess things up.
205 pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) {
209 let buf = slice::from_raw_parts(m as *const u8, s as _);
210 if let Ok(s) = str::from_utf8(&buf[..buf.iter().position(|&b| b == 0).unwrap_or(buf.len())]) {
217 // NB. used by both libunwind and libpanic_abort
218 pub unsafe extern "C" fn __rust_abort() {
219 crate::sys::abort_internal();
224 pub unsafe extern "C" fn __rust_c_alloc(size: usize, align: usize) -> *mut u8 {
225 alloc::alloc(Layout::from_size_align_unchecked(size, align))
230 pub unsafe extern "C" fn __rust_c_dealloc(ptr: *mut u8, size: usize, align: usize) {
231 alloc::dealloc(ptr, Layout::from_size_align_unchecked(size, align))
237 use core::array::FixedSizeArray;
238 use crate::mem::{self, MaybeUninit};
240 // Verify that the bytes of initialized RWLock are the same as in
241 // libunwind. If they change, `src/UnwindRustSgx.h` in libunwind needs to
244 fn test_c_rwlock_initializer() {
245 const RWLOCK_INIT: &[u8] = &[
246 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
247 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
248 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
249 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
250 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
251 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
252 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
253 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
254 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
255 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
256 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
257 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
258 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
259 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
260 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
261 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
266 test::black_box(MaybeUninit::<[RWLock; 16]>::zeroed());
270 unsafe fn rwlock_new(init: &mut MaybeUninit<RWLock>) {
271 init.set(RWLock::new());
275 // try hard to make sure that the padding/unused bytes in RWLock
276 // get initialized as 0. If the assertion below fails, that might
277 // just be an issue with the test code and not with the value of
280 let mut init = MaybeUninit::<RWLock>::zeroed();
281 rwlock_new(&mut init);
283 mem::transmute::<_, [u8; 128]>(init.into_initialized()).as_slice(),