OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2014 The Native Client Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 /* | |
8 * Native Client rwlock implementation | |
9 * | |
10 * This implementation is a 'write-prefering' reader-writer lock which | |
Mark Seaborn
2015/01/05 16:58:53
"preferring"
Sam Clegg
2015/01/06 19:06:24
Done.
| |
11 * avoids writer starvation by preventing readers from acquiring the lock | |
12 * while there are waiting writers. See: | |
13 * (http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock) | |
Mark Seaborn
2015/01/05 16:58:53
Nit: remove () brackets
Sam Clegg
2015/01/06 19:06:25
Done.
| |
14 * | |
15 * The thundering herd problem is avoided by only waking a single | |
16 * waiter (either a single writer or a single reader) when the | |
17 * lock is released. | |
18 */ | |
19 | |
20 #include <errno.h> | |
21 #include <unistd.h> | |
Mark Seaborn
2015/01/05 16:58:53
Is this used?
Sam Clegg
2015/01/06 19:06:25
Done.
| |
22 | |
23 #include "native_client/src/include/nacl_compiler_annotations.h" | |
Mark Seaborn
2015/01/05 16:58:53
Is this used?
Sam Clegg
2015/01/06 19:06:24
Done.
| |
24 #include "native_client/src/untrusted/nacl/nacl_irt.h" | |
Mark Seaborn
2015/01/05 16:58:53
Nit: Not used, AFAICT
Sam Clegg
2015/01/06 19:06:24
Done.
| |
25 #include "native_client/src/untrusted/pthread/pthread.h" | |
26 #include "native_client/src/untrusted/pthread/pthread_internal.h" | |
27 #include "native_client/src/untrusted/pthread/pthread_types.h" | |
28 | |
29 int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) { | |
30 attr->type = PTHREAD_PROCESS_PRIVATE; | |
31 return 0; | |
32 } | |
33 | |
34 int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) { | |
35 return 0; | |
36 } | |
37 | |
38 int pthread_rwlock_init(pthread_rwlock_t *rwlock, | |
39 const pthread_rwlockattr_t *attr) { | |
40 rwlock->writer_thread_id = NACL_PTHREAD_ILLEGAL_THREAD_ID; | |
41 rwlock->writers_waiting = 0; | |
42 rwlock->reader_count = 0; | |
43 int rc = pthread_mutex_init(&rwlock->mutex, NULL); | |
44 if (rc != 0) | |
45 return rc; | |
46 rc = pthread_cond_init(&rwlock->write_possible, NULL); | |
47 if (rc != 0) | |
48 return rc; | |
49 return pthread_cond_init(&rwlock->read_possible, NULL); | |
50 } | |
51 | |
52 /* | |
53 * Helper function used by waiting writers to determine if they can take | |
54 * the lock. The rwlock->mutex must be held when calling this function. | |
55 * Returns 1 if the write lock can be taken, 0 it it can't. | |
Mark Seaborn
2015/01/05 16:58:53
"if it"
Sam Clegg
2015/01/06 19:06:24
Done.
| |
56 */ | |
57 static inline int write_lock_available(pthread_rwlock_t *rwlock) { | |
58 /* | |
59 * Write lock is available if there is no current writer and no current | |
60 * readers. | |
61 */ | |
62 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) | |
63 return 0; | |
64 if (rwlock->reader_count > 0) | |
65 return 0; | |
66 return 1; | |
67 } | |
68 | |
69 /* | |
70 * Helper function used by waiting readers to determine if they can take | |
71 * the lock. The rwlock->mutex must be held when calling this function. | |
72 * Returns 1 if the write lock can be taken, 0 it it can't. | |
Mark Seaborn
2015/01/05 16:58:53
Ditto
Sam Clegg
2015/01/06 19:06:24
Done.
| |
73 */ | |
74 static inline int read_lock_available(pthread_rwlock_t *rwlock) { | |
75 /* | |
76 * Read lock is available if there is no current writer and no *waiting* | |
77 * writers. | |
78 */ | |
79 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) | |
80 return 0; | |
81 if (rwlock->writers_waiting > 0) | |
82 return 0; | |
83 return 1; | |
84 } | |
85 | |
86 /* | |
87 * Internal function used to acquire the read lock. | |
88 * This operates is three differnet ways in order to implement the three public | |
Mark Seaborn
2015/01/05 16:58:53
"operates in", "different"
Sam Clegg
2015/01/06 19:06:24
Done.
| |
89 * functions: | |
90 * pthread_rwlock_rdlock | |
91 * pthread_rwlock_tryrdlock | |
92 * pthread_rwlock_timedrdlock | |
93 */ | |
94 static int rdlock_internal(pthread_rwlock_t *rwlock, | |
95 const struct timespec *abs_timeout, | |
96 int try_only) { | |
97 int rc = pthread_mutex_lock(&rwlock->mutex); | |
98 if (rc != 0) | |
99 return rc; | |
100 | |
101 /* | |
102 * Wait repeatedly until the write preconditions are met. | |
103 * In theory this loop should only execute once because the preconditions | |
104 * should always be true when the condition is signaled. | |
105 */ | |
106 while (!read_lock_available(rwlock)) { | |
107 if (try_only) { | |
108 rc = EBUSY; | |
109 } else if (abs_timeout != NULL) { | |
110 rc = pthread_cond_timedwait(&rwlock->read_possible, | |
111 &rwlock->mutex, | |
112 abs_timeout); | |
113 | |
Mark Seaborn
2015/01/05 16:58:53
Remove empty line
Sam Clegg
2015/01/06 19:06:24
Done.
| |
114 } else { | |
115 rc = pthread_cond_wait(&rwlock->read_possible, &rwlock->mutex); | |
116 } | |
117 if (rc != 0) | |
118 goto done; | |
119 } | |
120 | |
121 /* Acquire the read lock. */ | |
122 rwlock->reader_count++; | |
123 done: | |
124 pthread_mutex_unlock(&rwlock->mutex); | |
125 return rc; | |
126 } | |
127 | |
128 /* | |
129 * Internal function used to acquire the write lock. | |
130 * This operates is three differnet ways in order to implement the three public | |
Mark Seaborn
2015/01/05 16:58:53
Same here
Sam Clegg
2015/01/06 19:06:24
Done.
| |
131 * functions: | |
132 * pthread_rwlock_wrlock | |
133 * pthread_rwlock_trywrlock | |
134 * pthread_rwlock_timedwrlock | |
135 */ | |
136 static int rwlock_internal(pthread_rwlock_t *rwlock, | |
137 const struct timespec *abs_timeout, | |
138 int try_only) { | |
139 int rc = pthread_mutex_lock(&rwlock->mutex); | |
140 if (rc != 0) | |
141 return rc; | |
142 | |
143 /* Wait repeatedly until the write preconditions are met */ | |
144 while (!write_lock_available(rwlock)) { | |
145 if (try_only) { | |
146 rc = EBUSY; | |
147 } else { | |
148 /* | |
149 * Before waiting (and releasing the lock) we increment the | |
150 * waiting_writers count so the unlocking code knows to wake | |
151 * us first (before any waiting readers). | |
152 */ | |
153 rwlock->writers_waiting++; | |
154 if (abs_timeout) { | |
Mark Seaborn
2015/01/05 16:58:53
Style nit: Add "!= NULL"
Sam Clegg
2015/01/06 19:06:24
Done.
| |
155 rc = pthread_cond_timedwait(&rwlock->write_possible, | |
156 &rwlock->mutex, | |
157 abs_timeout); | |
158 } else { | |
159 rc = pthread_cond_wait(&rwlock->write_possible, | |
160 &rwlock->mutex); | |
161 } | |
162 rwlock->writers_waiting--; | |
163 } | |
164 if (rc != 0) | |
165 goto done; | |
166 } | |
167 | |
168 /* Acquire the write lock. */ | |
169 rwlock->writer_thread_id = pthread_self(); | |
170 done: | |
171 pthread_mutex_unlock(&rwlock->mutex); | |
Mark Seaborn
2015/01/05 16:58:53
You should really check the return value here too.
Sam Clegg
2015/01/06 19:06:25
Done.
| |
172 return rc; | |
173 } | |
174 | |
175 int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, | |
176 const struct timespec *abs_timeout) { | |
177 return rdlock_internal(rwlock, abs_timeout, 0); | |
178 } | |
179 | |
180 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { | |
181 return rdlock_internal(rwlock, NULL, 0); | |
182 } | |
183 | |
184 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) { | |
185 return rdlock_internal(rwlock, NULL, 1); | |
186 } | |
187 | |
188 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { | |
189 return rwlock_internal(rwlock, NULL, 0); | |
190 } | |
191 | |
192 int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, | |
193 const struct timespec *abs_timeout) { | |
194 return rwlock_internal(rwlock, abs_timeout, 0); | |
195 } | |
196 | |
197 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) { | |
198 return rwlock_internal(rwlock, NULL, 1); | |
199 } | |
200 | |
201 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { | |
202 int rc = pthread_mutex_lock(&rwlock->mutex); | |
203 if (rc != 0) | |
204 return rc; | |
205 | |
206 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) { | |
207 /* The write lock is held. Ensure its us that holds it. */ | |
Mark Seaborn
2015/01/05 16:58:53
"it's"
"us" is vague. How about "Ensure it's the
Sam Clegg
2015/01/06 19:06:24
Done.
| |
208 if (rwlock->writer_thread_id != pthread_self()) { | |
209 rc = EPERM; | |
210 goto done; | |
211 } | |
212 | |
213 /* Release write lock. */ | |
214 rwlock->writer_thread_id = NACL_PTHREAD_ILLEGAL_THREAD_ID; | |
215 if (rwlock->writers_waiting > 0) { | |
216 /* Wake a waiting writer if there is one. */ | |
217 rc = pthread_cond_signal(&rwlock->write_possible); | |
218 } else { | |
219 /* Otherwise wake a waiting reader. */ | |
220 rc = pthread_cond_signal(&rwlock->read_possible); | |
221 } | |
222 } else { | |
223 if (rwlock->reader_count == 0) { | |
224 rc = EPERM; | |
225 goto done; | |
226 } | |
227 | |
228 /* Release read lock. */ | |
229 rwlock->reader_count--; | |
230 if (rwlock->reader_count == 0 && rwlock->writers_waiting > 0) { | |
231 /* Wake a waiting writer. */ | |
232 rc = pthread_cond_signal(&rwlock->write_possible); | |
233 } | |
234 } | |
235 | |
236 done: | |
237 pthread_mutex_unlock(&rwlock->mutex); | |
Mark Seaborn
2015/01/05 16:58:53
Ditto: check return value
Sam Clegg
2015/01/06 19:06:25
Done.
| |
238 return rc; | |
239 } | |
240 | |
241 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { | |
242 /* Return EBUSY if anther thread hold the mutex */ | |
Mark Seaborn
2015/01/05 16:58:53
"another", "holds". (Can you proof-read your comm
Sam Clegg
2015/01/06 19:06:24
Done.
| |
243 int rc = pthread_mutex_trylock(&rwlock->mutex); | |
244 if (rc != 0) { | |
245 return rc; | |
246 } | |
247 | |
248 /* Return EBUSY if there are current readers or a current writer */ | |
249 if (rwlock->reader_count != 0) { | |
250 pthread_mutex_unlock(&rwlock->mutex); | |
251 return EBUSY; | |
252 } | |
253 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) { | |
254 pthread_mutex_unlock(&rwlock->mutex); | |
255 return EBUSY; | |
256 } | |
257 | |
258 int rc1 = pthread_cond_destroy(&rwlock->write_possible); | |
259 int rc2 = pthread_cond_destroy(&rwlock->read_possible); | |
260 | |
261 /* Finally unlock the mutex and destroy it */ | |
262 pthread_mutex_unlock(&rwlock->mutex); | |
263 int rc3 = pthread_mutex_destroy(&rwlock->mutex); | |
264 if (rc1 != 0) | |
265 return rc1; | |
266 if (rc2 != 0) | |
267 return rc2; | |
268 return rc3; | |
269 } | |
OLD | NEW |