]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/sgx/rwlock.rs
4cba36aa64dd53992605d567ca892d1c99b7961e
[rust.git] / src / libstd / sys / sgx / rwlock.rs
1 #[cfg(not(test))]
2 use crate::alloc::{self, Layout};
3 use crate::num::NonZeroUsize;
4 #[cfg(not(test))]
5 use crate::slice;
6 #[cfg(not(test))]
7 use crate::str;
8
9 use super::waitqueue::{
10     try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable,
11 };
12 use crate::mem;
13
14 pub struct RWLock {
15     readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>,
16     writer: SpinMutex<WaitVariable<bool>>,
17 }
18
19 // Below is to check at compile time, that RWLock has size of 128 bytes.
20 #[allow(dead_code)]
21 unsafe fn rw_lock_size_assert(r: RWLock) {
22     mem::transmute::<RWLock, [u8; 128]>(r);
23 }
24
25 impl RWLock {
26     pub const fn new() -> RWLock {
27         RWLock {
28             readers: SpinMutex::new(WaitVariable::new(None)),
29             writer: SpinMutex::new(WaitVariable::new(false)),
30         }
31     }
32
33     #[inline]
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
39             drop(wguard);
40             WaitQueue::wait(rguard);
41             // Another thread has passed the lock to us
42         } else {
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);
46         }
47     }
48
49     #[inline]
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
55             false
56         } else {
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);
60             true
61         }
62     }
63
64     #[inline]
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
70             drop(rguard);
71             WaitQueue::wait(wguard);
72             // Another thread has passed the lock to us
73         } else {
74             // We are just now obtaining the lock
75             *wguard.lock_var_mut() = true;
76         }
77     }
78
79     #[inline]
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
85             false
86         } else {
87             // We are just now obtaining the lock
88             *wguard.lock_var_mut() = true;
89             true
90         }
91     }
92
93     #[inline]
94     unsafe fn __read_unlock(
95         &self,
96         mut rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>,
97         wguard: SpinMutexGuard<'_, WaitVariable<bool>>,
98     ) {
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
102         } else {
103             if let Ok(mut wguard) = WaitQueue::notify_one(wguard) {
104                 // A writer was waiting, pass the lock
105                 *wguard.lock_var_mut() = true;
106             } else {
107                 // No writers were waiting, the lock is released
108                 assert!(rguard.queue_empty());
109             }
110         }
111     }
112
113     #[inline]
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);
118     }
119
120     #[inline]
121     unsafe fn __write_unlock(
122         &self,
123         rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>,
124         wguard: SpinMutexGuard<'_, WaitVariable<bool>>,
125     ) {
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)
133                 } else {
134                     unreachable!() // called notify_all
135                 }
136             } else {
137                 // No readers waiting, the lock is released
138             }
139         } else {
140             // There was a thread waiting for write, just pass the lock
141         }
142     }
143
144     #[inline]
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);
149     }
150
151     // only used by __rust_rwlock_unlock below
152     #[inline]
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);
159         } else {
160             self.__read_unlock(rguard, wguard);
161         }
162     }
163
164     #[inline]
165     pub unsafe fn destroy(&self) {}
166 }
167
168 #[cfg(not(test))]
169 const EINVAL: i32 = 22;
170
171 // used by libunwind port
172 #[cfg(not(test))]
173 #[no_mangle]
174 pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 {
175     if p.is_null() {
176         return EINVAL;
177     }
178     (*p).read();
179     return 0;
180 }
181
182 #[cfg(not(test))]
183 #[no_mangle]
184 pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 {
185     if p.is_null() {
186         return EINVAL;
187     }
188     (*p).write();
189     return 0;
190 }
191 #[cfg(not(test))]
192 #[no_mangle]
193 pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 {
194     if p.is_null() {
195         return EINVAL;
196     }
197     (*p).unlock();
198     return 0;
199 }
200
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.
203 #[cfg(not(test))]
204 #[no_mangle]
205 pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) {
206     if s < 0 {
207         return;
208     }
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())]) {
211         eprint!("{}", s);
212     }
213 }
214
215 #[cfg(not(test))]
216 #[no_mangle]
217 // NB. used by both libunwind and libpanic_abort
218 pub unsafe extern "C" fn __rust_abort() {
219     crate::sys::abort_internal();
220 }
221
222 #[cfg(not(test))]
223 #[no_mangle]
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))
226 }
227
228 #[cfg(not(test))]
229 #[no_mangle]
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))
232 }
233
234 #[cfg(test)]
235 mod tests {
236     use super::*;
237     use core::array::FixedSizeArray;
238     use crate::mem::{self, MaybeUninit};
239
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
242     // be changed too.
243     #[test]
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,
262         ];
263
264         #[inline(never)]
265         fn zero_stack() {
266             test::black_box(MaybeUninit::<[RWLock; 16]>::zeroed());
267         }
268
269         #[inline(never)]
270         unsafe fn rwlock_new(init: &mut MaybeUninit<RWLock>) {
271             init.write(RWLock::new());
272         }
273
274         unsafe {
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
278             // RWLOCK_INIT.
279             zero_stack();
280             let mut init = MaybeUninit::<RWLock>::zeroed();
281             rwlock_new(&mut init);
282             assert_eq!(
283                 mem::transmute::<_, [u8; 128]>(init.assume_init()).as_slice(),
284                 RWLOCK_INIT
285             )
286         };
287     }
288 }