]>
Commit | Line | Data |
---|---|---|
1b1a35ee XL |
1 | #[cfg(test)] |
2 | mod tests; | |
3 | ||
532ac7d7 | 4 | use crate::num::NonZeroUsize; |
0731742a XL |
5 | |
6 | use super::waitqueue::{ | |
7 | try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable, | |
8 | }; | |
532ac7d7 | 9 | use crate::mem; |
0731742a XL |
10 | |
11 | pub struct RWLock { | |
12 | readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>, | |
13 | writer: SpinMutex<WaitVariable<bool>>, | |
14 | } | |
15 | ||
74b04a01 | 16 | // Check at compile time that RWLock size matches C definition (see test_c_rwlock_initializer below) |
0731742a XL |
17 | #[allow(dead_code)] |
18 | unsafe fn rw_lock_size_assert(r: RWLock) { | |
74b04a01 | 19 | mem::transmute::<RWLock, [u8; 144]>(r); |
0731742a XL |
20 | } |
21 | ||
0731742a XL |
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); | |
dfeec247 XL |
37 | WaitQueue::wait(rguard, || {}); |
38 | // Another thread has passed the lock to us | |
0731742a XL |
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); | |
dfeec247 XL |
68 | WaitQueue::wait(wguard, || {}); |
69 | // Another thread has passed the lock to us | |
0731742a XL |
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, | |
532ac7d7 XL |
93 | mut rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>, |
94 | wguard: SpinMutexGuard<'_, WaitVariable<bool>>, | |
0731742a XL |
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; | |
e1599b0c | 103 | wguard.drop_after(rguard); |
0731742a XL |
104 | } else { |
105 | // No writers were waiting, the lock is released | |
532ac7d7 | 106 | rtassert!(rguard.queue_empty()); |
0731742a XL |
107 | } |
108 | } | |
109 | } | |
110 | ||
111 | #[inline] | |
112 | pub unsafe fn read_unlock(&self) { | |
113 | let rguard = self.readers.lock(); | |
114 | let wguard = self.writer.lock(); | |
115 | self.__read_unlock(rguard, wguard); | |
116 | } | |
117 | ||
118 | #[inline] | |
119 | unsafe fn __write_unlock( | |
120 | &self, | |
532ac7d7 XL |
121 | rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>, |
122 | wguard: SpinMutexGuard<'_, WaitVariable<bool>>, | |
0731742a | 123 | ) { |
e1599b0c XL |
124 | match WaitQueue::notify_one(wguard) { |
125 | Err(mut wguard) => { | |
126 | // No writers waiting, release the write lock | |
127 | *wguard.lock_var_mut() = false; | |
128 | if let Ok(mut rguard) = WaitQueue::notify_all(rguard) { | |
129 | // One or more readers were waiting, pass the lock to them | |
130 | if let NotifiedTcs::All { count } = rguard.notified_tcs() { | |
131 | *rguard.lock_var_mut() = Some(count) | |
132 | } else { | |
133 | unreachable!() // called notify_all | |
134 | } | |
135 | rguard.drop_after(wguard); | |
0731742a | 136 | } else { |
e1599b0c | 137 | // No readers waiting, the lock is released |
0731742a | 138 | } |
dfeec247 | 139 | } |
e1599b0c XL |
140 | Ok(wguard) => { |
141 | // There was a thread waiting for write, just pass the lock | |
142 | wguard.drop_after(rguard); | |
0731742a | 143 | } |
0731742a XL |
144 | } |
145 | } | |
146 | ||
147 | #[inline] | |
148 | pub unsafe fn write_unlock(&self) { | |
149 | let rguard = self.readers.lock(); | |
150 | let wguard = self.writer.lock(); | |
151 | self.__write_unlock(rguard, wguard); | |
152 | } | |
153 | ||
9fa01778 | 154 | // only used by __rust_rwlock_unlock below |
0731742a | 155 | #[inline] |
532ac7d7 | 156 | #[cfg_attr(test, allow(dead_code))] |
0731742a XL |
157 | unsafe fn unlock(&self) { |
158 | let rguard = self.readers.lock(); | |
159 | let wguard = self.writer.lock(); | |
160 | if *wguard.lock_var() == true { | |
161 | self.__write_unlock(rguard, wguard); | |
162 | } else { | |
163 | self.__read_unlock(rguard, wguard); | |
164 | } | |
165 | } | |
166 | ||
167 | #[inline] | |
168 | pub unsafe fn destroy(&self) {} | |
169 | } | |
170 | ||
532ac7d7 XL |
171 | // The following functions are needed by libunwind. These symbols are named |
172 | // in pre-link args for the target specification, so keep that in sync. | |
173 | #[cfg(not(test))] | |
0731742a XL |
174 | const EINVAL: i32 = 22; |
175 | ||
532ac7d7 | 176 | #[cfg(not(test))] |
0731742a XL |
177 | #[no_mangle] |
178 | pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 { | |
179 | if p.is_null() { | |
180 | return EINVAL; | |
181 | } | |
182 | (*p).read(); | |
183 | return 0; | |
184 | } | |
185 | ||
532ac7d7 | 186 | #[cfg(not(test))] |
0731742a XL |
187 | #[no_mangle] |
188 | pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 { | |
189 | if p.is_null() { | |
190 | return EINVAL; | |
191 | } | |
192 | (*p).write(); | |
193 | return 0; | |
194 | } | |
532ac7d7 | 195 | #[cfg(not(test))] |
0731742a XL |
196 | #[no_mangle] |
197 | pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 { | |
198 | if p.is_null() { | |
199 | return EINVAL; | |
200 | } | |
201 | (*p).unlock(); | |
202 | return 0; | |
203 | } |