]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/sgx/rwlock.rs
33163a556c16df96f7e79e9ffaef52adb7ef0929
[rust.git] / src / libstd / sys / sgx / rwlock.rs
1 use alloc::{self, Layout};
2 use num::NonZeroUsize;
3 use slice;
4 use str;
5
6 use super::waitqueue::{
7     try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable,
8 };
9 use mem;
10
11 pub struct RWLock {
12     readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>,
13     writer: SpinMutex<WaitVariable<bool>>,
14 }
15
16 // Below is to check at compile time, that RWLock has size of 128 bytes.
17 #[allow(dead_code)]
18 unsafe fn rw_lock_size_assert(r: RWLock) {
19     mem::transmute::<RWLock, [u8; 128]>(r);
20 }
21
22 impl RWLock {
23     pub const fn new() -> RWLock {
24         RWLock {
25             readers: SpinMutex::new(WaitVariable::new(None)),
26             writer: SpinMutex::new(WaitVariable::new(false)),
27         }
28     }
29
30     #[inline]
31     pub unsafe fn read(&self) {
32         let mut rguard = self.readers.lock();
33         let wguard = self.writer.lock();
34         if *wguard.lock_var() || !wguard.queue_empty() {
35             // Another thread has or is waiting for the write lock, wait
36             drop(wguard);
37             WaitQueue::wait(rguard);
38             // Another thread has passed the lock to us
39         } else {
40             // No waiting writers, acquire the read lock
41             *rguard.lock_var_mut() =
42                 NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
43         }
44     }
45
46     #[inline]
47     pub unsafe fn try_read(&self) -> bool {
48         let mut rguard = try_lock_or_false!(self.readers);
49         let wguard = try_lock_or_false!(self.writer);
50         if *wguard.lock_var() || !wguard.queue_empty() {
51             // Another thread has or is waiting for the write lock
52             false
53         } else {
54             // No waiting writers, acquire the read lock
55             *rguard.lock_var_mut() =
56                 NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
57             true
58         }
59     }
60
61     #[inline]
62     pub unsafe fn write(&self) {
63         let rguard = self.readers.lock();
64         let mut wguard = self.writer.lock();
65         if *wguard.lock_var() || rguard.lock_var().is_some() {
66             // Another thread has the lock, wait
67             drop(rguard);
68             WaitQueue::wait(wguard);
69             // Another thread has passed the lock to us
70         } else {
71             // We are just now obtaining the lock
72             *wguard.lock_var_mut() = true;
73         }
74     }
75
76     #[inline]
77     pub unsafe fn try_write(&self) -> bool {
78         let rguard = try_lock_or_false!(self.readers);
79         let mut wguard = try_lock_or_false!(self.writer);
80         if *wguard.lock_var() || rguard.lock_var().is_some() {
81             // Another thread has the lock
82             false
83         } else {
84             // We are just now obtaining the lock
85             *wguard.lock_var_mut() = true;
86             true
87         }
88     }
89
90     #[inline]
91     unsafe fn __read_unlock(
92         &self,
93         mut rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>,
94         wguard: SpinMutexGuard<WaitVariable<bool>>,
95     ) {
96         *rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1);
97         if rguard.lock_var().is_some() {
98             // There are other active readers
99         } else {
100             if let Ok(mut wguard) = WaitQueue::notify_one(wguard) {
101                 // A writer was waiting, pass the lock
102                 *wguard.lock_var_mut() = true;
103             } else {
104                 // No writers were waiting, the lock is released
105                 assert!(rguard.queue_empty());
106             }
107         }
108     }
109
110     #[inline]
111     pub unsafe fn read_unlock(&self) {
112         let rguard = self.readers.lock();
113         let wguard = self.writer.lock();
114         self.__read_unlock(rguard, wguard);
115     }
116
117     #[inline]
118     unsafe fn __write_unlock(
119         &self,
120         rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>,
121         wguard: SpinMutexGuard<WaitVariable<bool>>,
122     ) {
123         if let Err(mut wguard) = WaitQueue::notify_one(wguard) {
124             // No writers waiting, release the write lock
125             *wguard.lock_var_mut() = false;
126             if let Ok(mut rguard) = WaitQueue::notify_all(rguard) {
127                 // One or more readers were waiting, pass the lock to them
128                 if let NotifiedTcs::All { count } = rguard.notified_tcs() {
129                     *rguard.lock_var_mut() = Some(count)
130                 } else {
131                     unreachable!() // called notify_all
132                 }
133             } else {
134                 // No readers waiting, the lock is released
135             }
136         } else {
137             // There was a thread waiting for write, just pass the lock
138         }
139     }
140
141     #[inline]
142     pub unsafe fn write_unlock(&self) {
143         let rguard = self.readers.lock();
144         let wguard = self.writer.lock();
145         self.__write_unlock(rguard, wguard);
146     }
147
148     // only used by __rust_rwlock_unlock below
149     #[inline]
150     unsafe fn unlock(&self) {
151         let rguard = self.readers.lock();
152         let wguard = self.writer.lock();
153         if *wguard.lock_var() == true {
154             self.__write_unlock(rguard, wguard);
155         } else {
156             self.__read_unlock(rguard, wguard);
157         }
158     }
159
160     #[inline]
161     pub unsafe fn destroy(&self) {}
162 }
163
164 const EINVAL: i32 = 22;
165
166 // used by libunwind port
167 #[no_mangle]
168 pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 {
169     if p.is_null() {
170         return EINVAL;
171     }
172     (*p).read();
173     return 0;
174 }
175
176 #[no_mangle]
177 pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 {
178     if p.is_null() {
179         return EINVAL;
180     }
181     (*p).write();
182     return 0;
183 }
184 #[no_mangle]
185 pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 {
186     if p.is_null() {
187         return EINVAL;
188     }
189     (*p).unlock();
190     return 0;
191 }
192
193 // the following functions are also used by the libunwind port. They're
194 // included here to make sure parallel codegen and LTO don't mess things up.
195 #[no_mangle]
196 pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) {
197     if s < 0 {
198         return;
199     }
200     let buf = slice::from_raw_parts(m as *const u8, s as _);
201     if let Ok(s) = str::from_utf8(&buf[..buf.iter().position(|&b| b == 0).unwrap_or(buf.len())]) {
202         eprint!("{}", s);
203     }
204 }
205
206 #[no_mangle]
207 pub unsafe extern "C" fn __rust_abort() {
208     ::sys::abort_internal();
209 }
210
211 #[no_mangle]
212 pub unsafe extern "C" fn __rust_c_alloc(size: usize, align: usize) -> *mut u8 {
213     alloc::alloc(Layout::from_size_align_unchecked(size, align))
214 }
215
216 #[no_mangle]
217 pub unsafe extern "C" fn __rust_c_dealloc(ptr: *mut u8, size: usize, align: usize) {
218     alloc::dealloc(ptr, Layout::from_size_align_unchecked(size, align))
219 }
220
221 #[cfg(test)]
222 mod tests {
223
224     use super::*;
225     use core::array::FixedSizeArray;
226     use mem::MaybeUninit;
227     use {mem, ptr};
228
229     // The below test verifies that the bytes of initialized RWLock are the ones
230     // we use in libunwind.
231     // If they change we need to update src/UnwindRustSgx.h in libunwind.
232     #[test]
233     fn test_c_rwlock_initializer() {
234         const RWLOCK_INIT: &[u8] = &[
235             0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
236             0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
237             0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
238             0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
239             0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
240             0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
241             0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
242             0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
243             0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
244             0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
245             0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
246             0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
247             0x3, 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             0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
251         ];
252
253         let mut init = MaybeUninit::<RWLock>::zeroed();
254         init.set(RWLock::new());
255         assert_eq!(
256             mem::transmute::<_, [u8; 128]>(init.into_inner()).as_slice(),
257             RWLOCK_INIT
258         );
259     }
260 }