Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(304)

Side by Side Diff: src/untrusted/pthread/nc_rwlock.c

Issue 623863002: Implement pthread_rwlock functions for NaCl newlib. (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/untrusted/pthread/nacl.scons ('k') | src/untrusted/pthread/pthread.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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-preferring' reader-writer lock which
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
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
22 #include "native_client/src/untrusted/pthread/pthread.h"
23 #include "native_client/src/untrusted/pthread/pthread_internal.h"
24 #include "native_client/src/untrusted/pthread/pthread_types.h"
25
26 int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) {
27 attr->type = PTHREAD_PROCESS_PRIVATE;
28 return 0;
29 }
30
31 int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) {
32 return 0;
33 }
34
35 int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,
36 int *pshared) {
37 *pshared = attr->type;
38 return 0;
39 }
40
41 int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) {
42 if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
43 return EINVAL;
44 attr->type = pshared;
45 return 0;
46 }
47
48 int pthread_rwlock_init(pthread_rwlock_t *rwlock,
49 const pthread_rwlockattr_t *attr) {
50 if (attr != NULL && attr->type != PTHREAD_PROCESS_PRIVATE)
51 return EINVAL;
52 rwlock->writer_thread_id = NACL_PTHREAD_ILLEGAL_THREAD_ID;
53 rwlock->writers_waiting = 0;
54 rwlock->reader_count = 0;
55 int rc = pthread_mutex_init(&rwlock->mutex, NULL);
56 if (rc != 0)
57 return rc;
58 rc = pthread_cond_init(&rwlock->write_possible, NULL);
59 if (rc != 0)
60 return rc;
61 return pthread_cond_init(&rwlock->read_possible, NULL);
62 }
63
64 /*
65 * Helper function used by waiting writers to determine if they can take
66 * the lock. The rwlock->mutex must be held when calling this function.
67 * Returns 1 if the write lock can be taken, 0 if it can't.
68 */
69 static inline int write_lock_available(pthread_rwlock_t *rwlock) {
70 /*
71 * Write lock is available if there is no current writer and no current
72 * readers.
73 */
74 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID)
75 return 0;
76 if (rwlock->reader_count > 0)
77 return 0;
78 return 1;
79 }
80
81 /*
82 * Helper function used by waiting readers to determine if they can take
83 * the lock. The rwlock->mutex must be held when calling this function.
84 * Returns 1 if the write lock can be taken, 0 if it can't.
85 */
86 static inline int read_lock_available(pthread_rwlock_t *rwlock) {
87 /*
88 * Read lock is available if there is no current writer and no *waiting*
89 * writers.
90 */
91 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID)
92 return 0;
93 if (rwlock->writers_waiting > 0)
Mark Seaborn 2015/01/29 00:40:37 I was ready to LG this when I noticed a potentiall
94 return 0;
95 return 1;
96 }
97
98 /*
99 * Internal function used to acquire the read lock.
100 * This operates in three different ways in order to implement the three public
101 * functions:
102 * pthread_rwlock_rdlock
103 * pthread_rwlock_tryrdlock
104 * pthread_rwlock_timedrdlock
105 */
106 static int rdlock_internal(pthread_rwlock_t *rwlock,
107 const struct timespec *abs_timeout,
108 int try_only) {
109 int rc2;
110 int rc = pthread_mutex_lock(&rwlock->mutex);
111 if (rc != 0)
112 return rc;
113
114 /*
115 * Wait repeatedly until the write preconditions are met.
116 * In theory this loop should only execute once because the preconditions
117 * should always be true when the condition is signaled.
118 */
119 while (!read_lock_available(rwlock)) {
120 if (try_only) {
121 rc = EBUSY;
122 } else if (abs_timeout != NULL) {
123 rc = pthread_cond_timedwait(&rwlock->read_possible,
124 &rwlock->mutex,
125 abs_timeout);
126 } else {
127 rc = pthread_cond_wait(&rwlock->read_possible, &rwlock->mutex);
128 }
129 if (rc != 0)
130 goto done;
131 }
132
133 /* Acquire the read lock. */
134 rwlock->reader_count++;
135 done:
136 rc2 = pthread_mutex_unlock(&rwlock->mutex);
137 return rc == 0 ? rc2 : rc;
138 }
139
140 /*
141 * Internal function used to acquire the write lock.
142 * This operates in three different ways in order to implement the three public
143 * functions:
144 * pthread_rwlock_wrlock
145 * pthread_rwlock_trywrlock
146 * pthread_rwlock_timedwrlock
147 */
148 static int rwlock_internal(pthread_rwlock_t *rwlock,
149 const struct timespec *abs_timeout,
150 int try_only) {
151 int rc2;
152 int rc = pthread_mutex_lock(&rwlock->mutex);
153 if (rc != 0)
154 return rc;
155
156 /* Wait repeatedly until the write preconditions are met */
157 while (!write_lock_available(rwlock)) {
158 if (try_only) {
159 rc = EBUSY;
160 } else {
161 /*
162 * Before waiting (and releasing the lock) we increment the
163 * waiting_writers count so the unlocking code knows to wake
164 * a writer first (before any waiting readers).
165 */
166 rwlock->writers_waiting++;
167 if (abs_timeout != NULL) {
168 rc = pthread_cond_timedwait(&rwlock->write_possible,
169 &rwlock->mutex,
170 abs_timeout);
171 } else {
172 rc = pthread_cond_wait(&rwlock->write_possible,
173 &rwlock->mutex);
174 }
175 rwlock->writers_waiting--;
176 }
177 if (rc != 0)
178 goto done;
179 }
180
181 /* Acquire the write lock. */
182 rwlock->writer_thread_id = pthread_self();
183 done:
184 rc2 = pthread_mutex_unlock(&rwlock->mutex);
185 return rc == 0 ? rc2 : rc;
186 }
187
188 int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
189 const struct timespec *abs_timeout) {
190 return rdlock_internal(rwlock, abs_timeout, 0);
191 }
192
193 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) {
194 return rdlock_internal(rwlock, NULL, 0);
195 }
196
197 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) {
198 return rdlock_internal(rwlock, NULL, 1);
199 }
200
201 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) {
202 return rwlock_internal(rwlock, NULL, 0);
203 }
204
205 int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock,
206 const struct timespec *abs_timeout) {
207 return rwlock_internal(rwlock, abs_timeout, 0);
208 }
209
210 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) {
211 return rwlock_internal(rwlock, NULL, 1);
212 }
213
214 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) {
215 int rc2;
216 int rc = pthread_mutex_lock(&rwlock->mutex);
217 if (rc != 0)
218 return rc;
219
220 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) {
221 /* The write lock is held. Ensure it's the current thread that holds it. */
222 if (rwlock->writer_thread_id != pthread_self()) {
223 rc = EPERM;
224 goto done;
225 }
226
227 /* Release write lock. */
228 rwlock->writer_thread_id = NACL_PTHREAD_ILLEGAL_THREAD_ID;
229 if (rwlock->writers_waiting > 0) {
230 /* Wake a waiting writer if there is one. */
231 rc = pthread_cond_signal(&rwlock->write_possible);
232 } else {
233 /* Otherwise wake a waiting reader. */
234 rc = pthread_cond_signal(&rwlock->read_possible);
235 }
236 } else {
237 if (rwlock->reader_count == 0) {
238 rc = EPERM;
239 goto done;
240 }
241
242 /* Release read lock. */
243 rwlock->reader_count--;
244 if (rwlock->reader_count == 0 && rwlock->writers_waiting > 0) {
245 /* Wake a waiting writer. */
246 rc = pthread_cond_signal(&rwlock->write_possible);
247 }
248 }
249
250 done:
251 rc2 = pthread_mutex_unlock(&rwlock->mutex);
252 return rc == 0 ? rc2 : rc;
253 }
254
255 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) {
256 /* Return EBUSY if another thread holds the mutex. */
257 int rc = pthread_mutex_trylock(&rwlock->mutex);
258 if (rc != 0) {
259 return rc;
260 }
261
262 /* Return EBUSY if there are active readers or an active writer. */
263 if (rwlock->reader_count != 0) {
264 pthread_mutex_unlock(&rwlock->mutex);
265 return EBUSY;
266 }
267 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) {
268 pthread_mutex_unlock(&rwlock->mutex);
269 return EBUSY;
270 }
271
272 int rc1 = pthread_cond_destroy(&rwlock->write_possible);
273 int rc2 = pthread_cond_destroy(&rwlock->read_possible);
274
275 /* Finally unlock the mutex and destroy it. */
276 pthread_mutex_unlock(&rwlock->mutex);
277 int rc3 = pthread_mutex_destroy(&rwlock->mutex);
278 if (rc1 != 0)
279 return rc1;
280 if (rc2 != 0)
281 return rc2;
282 return rc3;
283 }
OLDNEW
« no previous file with comments | « src/untrusted/pthread/nacl.scons ('k') | src/untrusted/pthread/pthread.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698