1 /* $OpenBSD: rthread_rwlock_compat.c,v 1.2 2022/05/14 14:52:20 cheloha Exp $ */
2 /*
3 * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org>
4 * Copyright (c) 2012 Philip Guenther <guenther@openbsd.org>
5 * All Rights Reserved.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19 /*
20 * rwlocks
21 */
22
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <errno.h>
27
28 #include <pthread.h>
29
30 #include "rthread.h"
31
32 static _atomic_lock_t rwlock_init_lock = _SPINLOCK_UNLOCKED;
33
34 int
pthread_rwlock_init(pthread_rwlock_t * lockp,const pthread_rwlockattr_t * attrp __unused)35 pthread_rwlock_init(pthread_rwlock_t *lockp,
36 const pthread_rwlockattr_t *attrp __unused)
37 {
38 pthread_rwlock_t lock;
39
40 lock = calloc(1, sizeof(*lock));
41 if (!lock)
42 return (errno);
43 lock->lock = _SPINLOCK_UNLOCKED;
44 TAILQ_INIT(&lock->writers);
45
46 *lockp = lock;
47
48 return (0);
49 }
50 DEF_STD(pthread_rwlock_init);
51
52 int
pthread_rwlock_destroy(pthread_rwlock_t * lockp)53 pthread_rwlock_destroy(pthread_rwlock_t *lockp)
54 {
55 pthread_rwlock_t lock;
56
57 assert(lockp);
58 lock = *lockp;
59 if (lock) {
60 if (lock->readers || !TAILQ_EMPTY(&lock->writers)) {
61 #define MSG "pthread_rwlock_destroy on rwlock with waiters!\n"
62 write(2, MSG, sizeof(MSG) - 1);
63 #undef MSG
64 return (EBUSY);
65 }
66 free(lock);
67 }
68 *lockp = NULL;
69
70 return (0);
71 }
72
73 static int
_rthread_rwlock_ensure_init(pthread_rwlock_t * lockp)74 _rthread_rwlock_ensure_init(pthread_rwlock_t *lockp)
75 {
76 int ret = 0;
77
78 /*
79 * If the rwlock is statically initialized, perform the dynamic
80 * initialization.
81 */
82 if (*lockp == NULL)
83 {
84 _spinlock(&rwlock_init_lock);
85 if (*lockp == NULL)
86 ret = pthread_rwlock_init(lockp, NULL);
87 _spinunlock(&rwlock_init_lock);
88 }
89 return (ret);
90 }
91
92
93 static int
_rthread_rwlock_rdlock(pthread_rwlock_t * lockp,const struct timespec * abstime,int try)94 _rthread_rwlock_rdlock(pthread_rwlock_t *lockp, const struct timespec *abstime,
95 int try)
96 {
97 pthread_rwlock_t lock;
98 pthread_t thread = pthread_self();
99 int error;
100
101 if ((error = _rthread_rwlock_ensure_init(lockp)))
102 return (error);
103
104 lock = *lockp;
105 _rthread_debug(5, "%p: rwlock_rdlock %p\n", (void *)thread,
106 (void *)lock);
107 _spinlock(&lock->lock);
108
109 /* writers have precedence */
110 if (lock->owner == NULL && TAILQ_EMPTY(&lock->writers))
111 lock->readers++;
112 else if (try)
113 error = EBUSY;
114 else if (lock->owner == thread)
115 error = EDEADLK;
116 else {
117 do {
118 if (__thrsleep(lock, CLOCK_REALTIME, abstime,
119 &lock->lock, NULL) == EWOULDBLOCK)
120 return (ETIMEDOUT);
121 _spinlock(&lock->lock);
122 } while (lock->owner != NULL || !TAILQ_EMPTY(&lock->writers));
123 lock->readers++;
124 }
125 _spinunlock(&lock->lock);
126
127 return (error);
128 }
129
130 int
pthread_rwlock_rdlock(pthread_rwlock_t * lockp)131 pthread_rwlock_rdlock(pthread_rwlock_t *lockp)
132 {
133 return (_rthread_rwlock_rdlock(lockp, NULL, 0));
134 }
135
136 int
pthread_rwlock_tryrdlock(pthread_rwlock_t * lockp)137 pthread_rwlock_tryrdlock(pthread_rwlock_t *lockp)
138 {
139 return (_rthread_rwlock_rdlock(lockp, NULL, 1));
140 }
141
142 int
pthread_rwlock_timedrdlock(pthread_rwlock_t * lockp,const struct timespec * abstime)143 pthread_rwlock_timedrdlock(pthread_rwlock_t *lockp,
144 const struct timespec *abstime)
145 {
146 if (abstime == NULL || !timespecisvalid(abstime))
147 return (EINVAL);
148 return (_rthread_rwlock_rdlock(lockp, abstime, 0));
149 }
150
151
152 static int
_rthread_rwlock_wrlock(pthread_rwlock_t * lockp,const struct timespec * abstime,int try)153 _rthread_rwlock_wrlock(pthread_rwlock_t *lockp, const struct timespec *abstime,
154 int try)
155 {
156 pthread_rwlock_t lock;
157 pthread_t thread = pthread_self();
158 int error;
159
160 if ((error = _rthread_rwlock_ensure_init(lockp)))
161 return (error);
162
163 lock = *lockp;
164
165 _rthread_debug(5, "%p: rwlock_timedwrlock %p\n", (void *)thread,
166 (void *)lock);
167 _spinlock(&lock->lock);
168 if (lock->readers == 0 && lock->owner == NULL)
169 lock->owner = thread;
170 else if (try)
171 error = EBUSY;
172 else if (lock->owner == thread)
173 error = EDEADLK;
174 else {
175 int do_wait;
176
177 /* gotta block */
178 TAILQ_INSERT_TAIL(&lock->writers, thread, waiting);
179 do {
180 do_wait = __thrsleep(thread, CLOCK_REALTIME, abstime,
181 &lock->lock, NULL) != EWOULDBLOCK;
182 _spinlock(&lock->lock);
183 } while (lock->owner != thread && do_wait);
184
185 if (lock->owner != thread) {
186 /* timed out, sigh */
187 TAILQ_REMOVE(&lock->writers, thread, waiting);
188 error = ETIMEDOUT;
189 }
190 }
191 _spinunlock(&lock->lock);
192
193 return (error);
194 }
195
196 int
pthread_rwlock_wrlock(pthread_rwlock_t * lockp)197 pthread_rwlock_wrlock(pthread_rwlock_t *lockp)
198 {
199 return (_rthread_rwlock_wrlock(lockp, NULL, 0));
200 }
201
202 int
pthread_rwlock_trywrlock(pthread_rwlock_t * lockp)203 pthread_rwlock_trywrlock(pthread_rwlock_t *lockp)
204 {
205 return (_rthread_rwlock_wrlock(lockp, NULL, 1));
206 }
207
208 int
pthread_rwlock_timedwrlock(pthread_rwlock_t * lockp,const struct timespec * abstime)209 pthread_rwlock_timedwrlock(pthread_rwlock_t *lockp,
210 const struct timespec *abstime)
211 {
212 if (abstime == NULL || !timespecisvalid(abstime))
213 return (EINVAL);
214 return (_rthread_rwlock_wrlock(lockp, abstime, 0));
215 }
216
217
218 int
pthread_rwlock_unlock(pthread_rwlock_t * lockp)219 pthread_rwlock_unlock(pthread_rwlock_t *lockp)
220 {
221 pthread_rwlock_t lock;
222 pthread_t thread = pthread_self();
223 pthread_t next;
224 int was_writer;
225
226 lock = *lockp;
227
228 _rthread_debug(5, "%p: rwlock_unlock %p\n", (void *)thread,
229 (void *)lock);
230 _spinlock(&lock->lock);
231 if (lock->owner != NULL) {
232 assert(lock->owner == thread);
233 was_writer = 1;
234 } else {
235 assert(lock->readers > 0);
236 lock->readers--;
237 if (lock->readers > 0)
238 goto out;
239 was_writer = 0;
240 }
241
242 lock->owner = next = TAILQ_FIRST(&lock->writers);
243 if (next != NULL) {
244 /* dequeue and wake first writer */
245 TAILQ_REMOVE(&lock->writers, next, waiting);
246 _spinunlock(&lock->lock);
247 __thrwakeup(next, 1);
248 return (0);
249 }
250
251 /* could there have been blocked readers? wake them all */
252 if (was_writer)
253 __thrwakeup(lock, 0);
254 out:
255 _spinunlock(&lock->lock);
256
257 return (0);
258 }
259