1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include "pthread_internal.h"
30 #include <errno.h>
31
32 /* Technical note:
33 *
34 * Possible states of a read/write lock:
35 *
36 * - no readers and no writer (unlocked)
37 * - one or more readers sharing the lock at the same time (read-locked)
38 * - one writer holding the lock (write-lock)
39 *
40 * Additionally:
41 * - trying to get the write-lock while there are any readers blocks
42 * - trying to get the read-lock while there is a writer blocks
43 * - a single thread can acquire the lock multiple times in the same mode
44 *
45 * - Posix states that behaviour is undefined it a thread tries to acquire
46 * the lock in two distinct modes (e.g. write after read, or read after write).
47 *
48 * - This implementation tries to avoid writer starvation by making the readers
49 * block as soon as there is a waiting writer on the lock. However, it cannot
50 * completely eliminate it: each time the lock is unlocked, all waiting threads
51 * are woken and battle for it, which one gets it depends on the kernel scheduler
52 * and is semi-random.
53 *
54 */
55
56 #define __likely(cond) __builtin_expect(!!(cond), 1)
57 #define __unlikely(cond) __builtin_expect(!!(cond), 0)
58
59 #define RWLOCKATTR_DEFAULT 0
60 #define RWLOCKATTR_SHARED_MASK 0x0010
61
62 extern pthread_internal_t* __get_thread(void);
63
64 /* Return a global kernel ID for the current thread */
__get_thread_id(void)65 static int __get_thread_id(void)
66 {
67 return __get_thread()->kernel_id;
68 }
69
pthread_rwlockattr_init(pthread_rwlockattr_t * attr)70 int pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
71 {
72 if (!attr)
73 return EINVAL;
74
75 *attr = PTHREAD_PROCESS_PRIVATE;
76 return 0;
77 }
78
pthread_rwlockattr_destroy(pthread_rwlockattr_t * attr)79 int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
80 {
81 if (!attr)
82 return EINVAL;
83
84 *attr = -1;
85 return 0;
86 }
87
pthread_rwlockattr_setpshared(pthread_rwlockattr_t * attr,int pshared)88 int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared)
89 {
90 if (!attr)
91 return EINVAL;
92
93 switch (pshared) {
94 case PTHREAD_PROCESS_PRIVATE:
95 case PTHREAD_PROCESS_SHARED:
96 *attr = pshared;
97 return 0;
98 default:
99 return EINVAL;
100 }
101 }
102
pthread_rwlockattr_getpshared(pthread_rwlockattr_t * attr,int * pshared)103 int pthread_rwlockattr_getpshared(pthread_rwlockattr_t *attr, int *pshared)
104 {
105 if (!attr || !pshared)
106 return EINVAL;
107
108 *pshared = *attr;
109 return 0;
110 }
111
pthread_rwlock_init(pthread_rwlock_t * rwlock,const pthread_rwlockattr_t * attr)112 int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
113 {
114 pthread_mutexattr_t* lock_attr = NULL;
115 pthread_condattr_t* cond_attr = NULL;
116 pthread_mutexattr_t lock_attr0;
117 pthread_condattr_t cond_attr0;
118 int ret;
119
120 if (rwlock == NULL)
121 return EINVAL;
122
123 if (attr && *attr == PTHREAD_PROCESS_SHARED) {
124 lock_attr = &lock_attr0;
125 pthread_mutexattr_init(lock_attr);
126 pthread_mutexattr_setpshared(lock_attr, PTHREAD_PROCESS_SHARED);
127
128 cond_attr = &cond_attr0;
129 pthread_condattr_init(cond_attr);
130 pthread_condattr_setpshared(cond_attr, PTHREAD_PROCESS_SHARED);
131 }
132
133 ret = pthread_mutex_init(&rwlock->lock, lock_attr);
134 if (ret != 0)
135 return ret;
136
137 ret = pthread_cond_init(&rwlock->cond, cond_attr);
138 if (ret != 0) {
139 pthread_mutex_destroy(&rwlock->lock);
140 return ret;
141 }
142
143 rwlock->numLocks = 0;
144 rwlock->pendingReaders = 0;
145 rwlock->pendingWriters = 0;
146 rwlock->writerThreadId = 0;
147
148 return 0;
149 }
150
pthread_rwlock_destroy(pthread_rwlock_t * rwlock)151 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
152 {
153 int ret;
154
155 if (rwlock == NULL)
156 return EINVAL;
157
158 if (rwlock->numLocks > 0)
159 return EBUSY;
160
161 pthread_cond_destroy(&rwlock->cond);
162 pthread_mutex_destroy(&rwlock->lock);
163 return 0;
164 }
165
166 /* Returns TRUE iff we can acquire a read lock. */
read_precondition(pthread_rwlock_t * rwlock,int thread_id)167 static __inline__ int read_precondition(pthread_rwlock_t *rwlock, int thread_id)
168 {
169 /* We can't have the lock if any writer is waiting for it (writer bias).
170 * This tries to avoid starvation when there are multiple readers racing.
171 */
172 if (rwlock->pendingWriters > 0)
173 return 0;
174
175 /* We can have the lock if there is no writer, or if we write-own it */
176 /* The second test avoids a self-dead lock in case of buggy code. */
177 if (rwlock->writerThreadId == 0 || rwlock->writerThreadId == thread_id)
178 return 1;
179
180 /* Otherwise, we can't have it */
181 return 0;
182 }
183
184 /* returns TRUE iff we can acquire a write lock. */
write_precondition(pthread_rwlock_t * rwlock,int thread_id)185 static __inline__ int write_precondition(pthread_rwlock_t *rwlock, int thread_id)
186 {
187 /* We can get the lock if nobody has it */
188 if (rwlock->numLocks == 0)
189 return 1;
190
191 /* Or if we already own it */
192 if (rwlock->writerThreadId == thread_id)
193 return 1;
194
195 /* Otherwise, not */
196 return 0;
197 }
198
199 /* This function is used to waken any waiting thread contending
200 * for the lock. One of them should be able to grab it after
201 * that.
202 */
_pthread_rwlock_pulse(pthread_rwlock_t * rwlock)203 static void _pthread_rwlock_pulse(pthread_rwlock_t *rwlock)
204 {
205 if (rwlock->pendingReaders > 0 || rwlock->pendingWriters > 0)
206 pthread_cond_broadcast(&rwlock->cond);
207 }
208
209
pthread_rwlock_rdlock(pthread_rwlock_t * rwlock)210 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
211 {
212 return pthread_rwlock_timedrdlock(rwlock, NULL);
213 }
214
pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock)215 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
216 {
217 int ret = 0;
218
219 if (rwlock == NULL)
220 return EINVAL;
221
222 pthread_mutex_lock(&rwlock->lock);
223 if (__unlikely(!read_precondition(rwlock, __get_thread_id())))
224 ret = EBUSY;
225 else
226 rwlock->numLocks ++;
227 pthread_mutex_unlock(&rwlock->lock);
228
229 return ret;
230 }
231
pthread_rwlock_timedrdlock(pthread_rwlock_t * rwlock,const struct timespec * abs_timeout)232 int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, const struct timespec *abs_timeout)
233 {
234 int thread_id, ret = 0;
235
236 if (rwlock == NULL)
237 return EINVAL;
238
239 pthread_mutex_lock(&rwlock->lock);
240 thread_id = __get_thread_id();
241 if (__unlikely(!read_precondition(rwlock, thread_id))) {
242 rwlock->pendingReaders += 1;
243 do {
244 ret = pthread_cond_timedwait(&rwlock->cond, &rwlock->lock, abs_timeout);
245 } while (ret == 0 && !read_precondition(rwlock, thread_id));
246 rwlock->pendingReaders -= 1;
247 if (ret != 0)
248 goto EXIT;
249 }
250 rwlock->numLocks ++;
251 EXIT:
252 pthread_mutex_unlock(&rwlock->lock);
253 return ret;
254 }
255
256
pthread_rwlock_wrlock(pthread_rwlock_t * rwlock)257 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
258 {
259 return pthread_rwlock_timedwrlock(rwlock, NULL);
260 }
261
pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock)262 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
263 {
264 int thread_id, ret = 0;
265
266 if (rwlock == NULL)
267 return EINVAL;
268
269 pthread_mutex_lock(&rwlock->lock);
270 thread_id = __get_thread_id();
271 if (__unlikely(!write_precondition(rwlock, thread_id))) {
272 ret = EBUSY;
273 } else {
274 rwlock->numLocks ++;
275 rwlock->writerThreadId = thread_id;
276 }
277 pthread_mutex_unlock(&rwlock->lock);
278 return ret;
279 }
280
pthread_rwlock_timedwrlock(pthread_rwlock_t * rwlock,const struct timespec * abs_timeout)281 int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const struct timespec *abs_timeout)
282 {
283 int thread_id, ret = 0;
284
285 if (rwlock == NULL)
286 return EINVAL;
287
288 pthread_mutex_lock(&rwlock->lock);
289 thread_id = __get_thread_id();
290 if (__unlikely(!write_precondition(rwlock, thread_id))) {
291 /* If we can't read yet, wait until the rwlock is unlocked
292 * and try again. Increment pendingReaders to get the
293 * cond broadcast when that happens.
294 */
295 rwlock->pendingWriters += 1;
296 do {
297 ret = pthread_cond_timedwait(&rwlock->cond, &rwlock->lock, abs_timeout);
298 } while (ret == 0 && !write_precondition(rwlock, thread_id));
299 rwlock->pendingWriters -= 1;
300 if (ret != 0)
301 goto EXIT;
302 }
303 rwlock->numLocks ++;
304 rwlock->writerThreadId = thread_id;
305 EXIT:
306 pthread_mutex_unlock(&rwlock->lock);
307 return ret;
308 }
309
310
pthread_rwlock_unlock(pthread_rwlock_t * rwlock)311 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
312 {
313 int ret = 0;
314
315 if (rwlock == NULL)
316 return EINVAL;
317
318 pthread_mutex_lock(&rwlock->lock);
319
320 /* The lock must be held */
321 if (rwlock->numLocks == 0) {
322 ret = EPERM;
323 goto EXIT;
324 }
325
326 /* If it has only readers, writerThreadId is 0 */
327 if (rwlock->writerThreadId == 0) {
328 if (--rwlock->numLocks == 0)
329 _pthread_rwlock_pulse(rwlock);
330 }
331 /* Otherwise, it has only a single writer, which
332 * must be ourselves.
333 */
334 else {
335 if (rwlock->writerThreadId != __get_thread_id()) {
336 ret = EPERM;
337 goto EXIT;
338 }
339 if (--rwlock->numLocks == 0) {
340 rwlock->writerThreadId = 0;
341 _pthread_rwlock_pulse(rwlock);
342 }
343 }
344 EXIT:
345 pthread_mutex_unlock(&rwlock->lock);
346 return ret;
347 }
348