]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/sgx/rwlock.rs
Rollup merge of #60609 - spastorino:be-explicit-on-mem-replace-doc, r=Centril
[rust.git] / src / libstd / sys / sgx / rwlock.rs
1 use crate::num::NonZeroUsize;
2
3 use super::waitqueue::{
4     try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable,
5 };
6 use crate::mem;
7
8 pub struct RWLock {
9     readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>,
10     writer: SpinMutex<WaitVariable<bool>>,
11 }
12
13 // Below is to check at compile time, that RWLock has size of 128 bytes.
14 #[allow(dead_code)]
15 unsafe fn rw_lock_size_assert(r: RWLock) {
16     mem::transmute::<RWLock, [u8; 128]>(r);
17 }
18
19 impl RWLock {
20     pub const fn new() -> RWLock {
21         RWLock {
22             readers: SpinMutex::new(WaitVariable::new(None)),
23             writer: SpinMutex::new(WaitVariable::new(false)),
24         }
25     }
26
27     #[inline]
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
33             drop(wguard);
34             WaitQueue::wait(rguard);
35             // Another thread has passed the lock to us
36         } else {
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);
40         }
41     }
42
43     #[inline]
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
49             false
50         } else {
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);
54             true
55         }
56     }
57
58     #[inline]
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
64             drop(rguard);
65             WaitQueue::wait(wguard);
66             // Another thread has passed the lock to us
67         } else {
68             // We are just now obtaining the lock
69             *wguard.lock_var_mut() = true;
70         }
71     }
72
73     #[inline]
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
79             false
80         } else {
81             // We are just now obtaining the lock
82             *wguard.lock_var_mut() = true;
83             true
84         }
85     }
86
87     #[inline]
88     unsafe fn __read_unlock(
89         &self,
90         mut rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>,
91         wguard: SpinMutexGuard<'_, WaitVariable<bool>>,
92     ) {
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
96         } else {
97             if let Ok(mut wguard) = WaitQueue::notify_one(wguard) {
98                 // A writer was waiting, pass the lock
99                 *wguard.lock_var_mut() = true;
100             } else {
101                 // No writers were waiting, the lock is released
102                 rtassert!(rguard.queue_empty());
103             }
104         }
105     }
106
107     #[inline]
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);
112     }
113
114     #[inline]
115     unsafe fn __write_unlock(
116         &self,
117         rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>,
118         wguard: SpinMutexGuard<'_, WaitVariable<bool>>,
119     ) {
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)
127                 } else {
128                     unreachable!() // called notify_all
129                 }
130             } else {
131                 // No readers waiting, the lock is released
132             }
133         } else {
134             // There was a thread waiting for write, just pass the lock
135         }
136     }
137
138     #[inline]
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);
143     }
144
145     // only used by __rust_rwlock_unlock below
146     #[inline]
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);
153         } else {
154             self.__read_unlock(rguard, wguard);
155         }
156     }
157
158     #[inline]
159     pub unsafe fn destroy(&self) {}
160 }
161
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.
164 #[cfg(not(test))]
165 const EINVAL: i32 = 22;
166
167 #[cfg(not(test))]
168 #[no_mangle]
169 pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 {
170     if p.is_null() {
171         return EINVAL;
172     }
173     (*p).read();
174     return 0;
175 }
176
177 #[cfg(not(test))]
178 #[no_mangle]
179 pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 {
180     if p.is_null() {
181         return EINVAL;
182     }
183     (*p).write();
184     return 0;
185 }
186 #[cfg(not(test))]
187 #[no_mangle]
188 pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 {
189     if p.is_null() {
190         return EINVAL;
191     }
192     (*p).unlock();
193     return 0;
194 }
195
196 #[cfg(test)]
197 mod tests {
198     use super::*;
199     use core::array::FixedSizeArray;
200     use crate::mem::{self, MaybeUninit};
201
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
204     // be changed too.
205     #[test]
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,
224         ];
225
226         #[inline(never)]
227         fn zero_stack() {
228             test::black_box(MaybeUninit::<[RWLock; 16]>::zeroed());
229         }
230
231         #[inline(never)]
232         unsafe fn rwlock_new(init: &mut MaybeUninit<RWLock>) {
233             init.write(RWLock::new());
234         }
235
236         unsafe {
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
240             // RWLOCK_INIT.
241             zero_stack();
242             let mut init = MaybeUninit::<RWLock>::zeroed();
243             rwlock_new(&mut init);
244             assert_eq!(
245                 mem::transmute::<_, [u8; 128]>(init.assume_init()).as_slice(),
246                 RWLOCK_INIT
247             )
248         };
249     }
250 }