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