]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/sgx/rwlock.rs
Rollup merge of #106273 - notriddle:notriddle/source-content-overflow, r=GuillaumeGomez
[rust.git] / library / std / src / sys / sgx / rwlock.rs
1 #[cfg(test)]
2 mod tests;
3
4 use crate::num::NonZeroUsize;
5 use crate::sys_common::lazy_box::{LazyBox, LazyInit};
6
7 use super::waitqueue::{
8     try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable,
9 };
10 use crate::alloc::Layout;
11
12 struct AllocatedRwLock {
13     readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>,
14     writer: SpinMutex<WaitVariable<bool>>,
15 }
16
17 pub struct RwLock {
18     inner: LazyBox<AllocatedRwLock>,
19 }
20
21 impl LazyInit for AllocatedRwLock {
22     fn init() -> Box<Self> {
23         Box::new(AllocatedRwLock {
24             readers: SpinMutex::new(WaitVariable::new(None)),
25             writer: SpinMutex::new(WaitVariable::new(false)),
26         })
27     }
28 }
29
30 // Check at compile time that RwLock's size and alignment matches the C definition
31 // in libunwind (see also `test_c_rwlock_initializer` in `tests`).
32 const _: () = {
33     let rust = Layout::new::<RwLock>();
34     let c = Layout::new::<*mut ()>();
35     assert!(rust.size() == c.size());
36     assert!(rust.align() == c.align());
37 };
38
39 impl RwLock {
40     pub const fn new() -> RwLock {
41         RwLock { inner: LazyBox::new() }
42     }
43
44     #[inline]
45     pub fn read(&self) {
46         let lock = &*self.inner;
47         let mut rguard = lock.readers.lock();
48         let wguard = lock.writer.lock();
49         if *wguard.lock_var() || !wguard.queue_empty() {
50             // Another thread has or is waiting for the write lock, wait
51             drop(wguard);
52             WaitQueue::wait(rguard, || {});
53         // Another thread has passed the lock to us
54         } else {
55             // No waiting writers, acquire the read lock
56             *rguard.lock_var_mut() =
57                 NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
58         }
59     }
60
61     #[inline]
62     pub unsafe fn try_read(&self) -> bool {
63         let lock = &*self.inner;
64         let mut rguard = try_lock_or_false!(lock.readers);
65         let wguard = try_lock_or_false!(lock.writer);
66         if *wguard.lock_var() || !wguard.queue_empty() {
67             // Another thread has or is waiting for the write lock
68             false
69         } else {
70             // No waiting writers, acquire the read lock
71             *rguard.lock_var_mut() =
72                 NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
73             true
74         }
75     }
76
77     #[inline]
78     pub fn write(&self) {
79         let lock = &*self.inner;
80         let rguard = lock.readers.lock();
81         let mut wguard = lock.writer.lock();
82         if *wguard.lock_var() || rguard.lock_var().is_some() {
83             // Another thread has the lock, wait
84             drop(rguard);
85             WaitQueue::wait(wguard, || {});
86         // Another thread has passed the lock to us
87         } else {
88             // We are just now obtaining the lock
89             *wguard.lock_var_mut() = true;
90         }
91     }
92
93     #[inline]
94     pub fn try_write(&self) -> bool {
95         let lock = &*self.inner;
96         let rguard = try_lock_or_false!(lock.readers);
97         let mut wguard = try_lock_or_false!(lock.writer);
98         if *wguard.lock_var() || rguard.lock_var().is_some() {
99             // Another thread has the lock
100             false
101         } else {
102             // We are just now obtaining the lock
103             *wguard.lock_var_mut() = true;
104             true
105         }
106     }
107
108     #[inline]
109     unsafe fn __read_unlock(
110         &self,
111         mut rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>,
112         wguard: SpinMutexGuard<'_, WaitVariable<bool>>,
113     ) {
114         *rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1);
115         if rguard.lock_var().is_some() {
116             // There are other active readers
117         } else {
118             if let Ok(mut wguard) = WaitQueue::notify_one(wguard) {
119                 // A writer was waiting, pass the lock
120                 *wguard.lock_var_mut() = true;
121                 wguard.drop_after(rguard);
122             } else {
123                 // No writers were waiting, the lock is released
124                 rtassert!(rguard.queue_empty());
125             }
126         }
127     }
128
129     #[inline]
130     pub unsafe fn read_unlock(&self) {
131         let lock = &*self.inner;
132         let rguard = lock.readers.lock();
133         let wguard = lock.writer.lock();
134         unsafe { self.__read_unlock(rguard, wguard) };
135     }
136
137     #[inline]
138     unsafe fn __write_unlock(
139         &self,
140         rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>,
141         wguard: SpinMutexGuard<'_, WaitVariable<bool>>,
142     ) {
143         match WaitQueue::notify_one(wguard) {
144             Err(mut wguard) => {
145                 // No writers waiting, release the write lock
146                 *wguard.lock_var_mut() = false;
147                 if let Ok(mut rguard) = WaitQueue::notify_all(rguard) {
148                     // One or more readers were waiting, pass the lock to them
149                     if let NotifiedTcs::All { count } = rguard.notified_tcs() {
150                         *rguard.lock_var_mut() = Some(count)
151                     } else {
152                         unreachable!() // called notify_all
153                     }
154                     rguard.drop_after(wguard);
155                 } else {
156                     // No readers waiting, the lock is released
157                 }
158             }
159             Ok(wguard) => {
160                 // There was a thread waiting for write, just pass the lock
161                 wguard.drop_after(rguard);
162             }
163         }
164     }
165
166     #[inline]
167     pub unsafe fn write_unlock(&self) {
168         let lock = &*self.inner;
169         let rguard = lock.readers.lock();
170         let wguard = lock.writer.lock();
171         unsafe { self.__write_unlock(rguard, wguard) };
172     }
173
174     // only used by __rust_rwlock_unlock below
175     #[inline]
176     #[cfg_attr(test, allow(dead_code))]
177     unsafe fn unlock(&self) {
178         let lock = &*self.inner;
179         let rguard = lock.readers.lock();
180         let wguard = lock.writer.lock();
181         if *wguard.lock_var() == true {
182             unsafe { self.__write_unlock(rguard, wguard) };
183         } else {
184             unsafe { self.__read_unlock(rguard, wguard) };
185         }
186     }
187 }
188
189 // The following functions are needed by libunwind. These symbols are named
190 // in pre-link args for the target specification, so keep that in sync.
191 #[cfg(not(test))]
192 const EINVAL: i32 = 22;
193
194 #[cfg(not(test))]
195 #[no_mangle]
196 pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RwLock) -> i32 {
197     if p.is_null() {
198         return EINVAL;
199     }
200     unsafe { (*p).read() };
201     return 0;
202 }
203
204 #[cfg(not(test))]
205 #[no_mangle]
206 pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RwLock) -> i32 {
207     if p.is_null() {
208         return EINVAL;
209     }
210     unsafe { (*p).write() };
211     return 0;
212 }
213
214 #[cfg(not(test))]
215 #[no_mangle]
216 pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RwLock) -> i32 {
217     if p.is_null() {
218         return EINVAL;
219     }
220     unsafe { (*p).unlock() };
221     return 0;
222 }