• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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