Cleanup the TLS implementation:
[dragonfly.git] / lib / libthread_xu / thread / thr_cond.c
CommitLineData
71b3fa15
DX
1/*
2 * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
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 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
10 * disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
9e2ee207 26 * $DragonFly: src/lib/libthread_xu/thread/thr_cond.c,v 1.4 2005/03/29 19:26:20 joerg Exp $
71b3fa15
DX
27 */
28
9e2ee207
JS
29#include <machine/tls.h>
30
71b3fa15
DX
31#include <stdlib.h>
32#include <errno.h>
33#include <string.h>
34#include <pthread.h>
35#include <limits.h>
36
37#include "thr_private.h"
38
39/*
40 * Prototypes
41 */
42static int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
43static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
44 const struct timespec *abstime, int cancel);
45static int cond_signal_common(pthread_cond_t *cond, int broadcast);
46
47/*
48 * Double underscore versions are cancellation points. Single underscore
49 * versions are not and are provided for libc internal usage (which
50 * shouldn't introduce cancellation points).
51 */
52__weak_reference(__pthread_cond_wait, pthread_cond_wait);
53__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
54
55__weak_reference(_pthread_cond_init, pthread_cond_init);
56__weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
57__weak_reference(_pthread_cond_signal, pthread_cond_signal);
58__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
59
60static int
61cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
62{
63 pthread_cond_t pcond;
64 int rval = 0;
65
66 if ((pcond = (pthread_cond_t)
67 malloc(sizeof(struct pthread_cond))) == NULL) {
68 rval = ENOMEM;
69 } else {
70 /*
71 * Initialise the condition variable structure:
72 */
73 _thr_umtx_init(&pcond->c_lock);
74 pcond->c_seqno = 0;
75 pcond->c_waiters = 0;
76 pcond->c_wakeups = 0;
77 if (cond_attr == NULL || *cond_attr == NULL) {
78 pcond->c_pshared = 0;
79 pcond->c_clockid = CLOCK_REALTIME;
80 } else {
81 pcond->c_pshared = (*cond_attr)->c_pshared;
82 pcond->c_clockid = (*cond_attr)->c_clockid;
83 }
84 *cond = pcond;
85 }
86 /* Return the completion status: */
87 return (rval);
88}
89
90static int
91init_static(struct pthread *thread, pthread_cond_t *cond)
92{
93 int ret;
94
95 THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
96
97 if (*cond == NULL)
98 ret = cond_init(cond, NULL);
99 else
100 ret = 0;
101
102 THR_LOCK_RELEASE(thread, &_cond_static_lock);
103
104 return (ret);
105}
106
107int
108_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
109{
110 *cond = NULL;
111 return cond_init(cond, cond_attr);
112}
113
114int
115_pthread_cond_destroy(pthread_cond_t *cond)
116{
117 struct pthread_cond *cv;
9e2ee207 118 struct pthread *curthread = tls_get_curthread();
71b3fa15
DX
119 int rval = 0;
120
121 if (*cond == NULL)
122 rval = EINVAL;
123 else {
124 /* Lock the condition variable structure: */
125 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
126 if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) {
127 THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
128 return (EBUSY);
129 }
130
131 /*
132 * NULL the caller's pointer now that the condition
133 * variable has been destroyed:
134 */
135 cv = *cond;
136 *cond = NULL;
137
138 /* Unlock the condition variable structure: */
139 THR_LOCK_RELEASE(curthread, &cv->c_lock);
140
141 /* Free the cond lock structure: */
142
143 /*
144 * Free the memory allocated for the condition
145 * variable structure:
146 */
147 free(cv);
148
149 }
150 /* Return the completion status: */
151 return (rval);
152}
153
154struct cond_cancel_info
155{
156 pthread_mutex_t *mutex;
157 pthread_cond_t *cond;
158 long seqno;
159};
160
161static void
162cond_cancel_handler(void *arg)
163{
9e2ee207 164 struct pthread *curthread = tls_get_curthread();
71b3fa15
DX
165 struct cond_cancel_info *cci = (struct cond_cancel_info *)arg;
166 pthread_cond_t cv;
167
168 cv = *(cci->cond);
169 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
170 if (cv->c_seqno != cci->seqno && cv->c_wakeups != 0) {
171 if (cv->c_waiters > 0) {
172 cv->c_seqno++;
173 _thr_umtx_wake(&cv->c_seqno, 1);
174 } else
175 cv->c_wakeups--;
176 } else {
177 cv->c_waiters--;
178 }
179 THR_LOCK_RELEASE(curthread, &cv->c_lock);
180
181 _mutex_cv_lock(cci->mutex);
182}
183
184static int
185cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
186 const struct timespec *abstime, int cancel)
187{
9e2ee207 188 struct pthread *curthread = tls_get_curthread();
71b3fa15
DX
189 struct timespec ts, ts2, *tsp;
190 struct cond_cancel_info cci;
191 pthread_cond_t cv;
192 long seq, oldseq;
193 int oldcancel;
194 int ret = 0;
195
196 /*
197 * If the condition variable is statically initialized,
198 * perform the dynamic initialization:
199 */
200 if (__predict_false(*cond == NULL &&
201 (ret = init_static(curthread, cond)) != 0))
202 return (ret);
203
204 cv = *cond;
205 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
206 ret = _mutex_cv_unlock(mutex);
207 if (ret) {
208 THR_LOCK_RELEASE(curthread, &cv->c_lock);
209 return (ret);
210 }
211 oldseq = seq = cv->c_seqno;
212 cci.mutex = mutex;
213 cci.cond = cond;
214 cci.seqno = oldseq;
215
216 cv->c_waiters++;
217 do {
218 THR_LOCK_RELEASE(curthread, &cv->c_lock);
219
220 if (abstime != NULL) {
221 clock_gettime(cv->c_clockid, &ts);
222 TIMESPEC_SUB(&ts2, abstime, &ts);
223 tsp = &ts2;
224 } else
225 tsp = NULL;
226
227 if (cancel) {
228 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &cci);
229 oldcancel = _thr_cancel_enter(curthread);
9219c44c
DX
230 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp,
231 cv->c_clockid);
71b3fa15
DX
232 _thr_cancel_leave(curthread, oldcancel);
233 THR_CLEANUP_POP(curthread, 0);
234 } else {
9219c44c
DX
235 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp,
236 cv->c_clockid);
71b3fa15
DX
237 }
238
239 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
240 seq = cv->c_seqno;
241 if (abstime != NULL && ret == ETIMEDOUT)
242 break;
243
244 /*
245 * loop if we have never been told to wake up
246 * or we lost a race.
247 */
248 } while (seq == oldseq || cv->c_wakeups == 0);
249
250 if (seq != oldseq && cv->c_wakeups != 0) {
251 cv->c_wakeups--;
252 ret = 0;
253 } else {
254 cv->c_waiters--;
255 }
256 THR_LOCK_RELEASE(curthread, &cv->c_lock);
257 _mutex_cv_lock(mutex);
258 return (ret);
259}
260
261int
262_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
263{
36750816
DX
264
265 return (cond_wait_common(cond, mutex, NULL, 0));
71b3fa15
DX
266}
267
268__strong_reference(_pthread_cond_wait, _thr_cond_wait);
269
270int
271__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
272{
71b3fa15 273
36750816 274 return (cond_wait_common(cond, mutex, NULL, 1));
71b3fa15
DX
275}
276
277int
278_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
279 const struct timespec * abstime)
280{
281 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
282 abstime->tv_nsec >= 1000000000)
283 return (EINVAL);
284
36750816 285 return (cond_wait_common(cond, mutex, abstime, 0));
71b3fa15
DX
286}
287
288__strong_reference(_pthread_cond_timedwait, _thr_cond_timedwait);
289
290int
291__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
292 const struct timespec *abstime)
293{
294 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
295 abstime->tv_nsec >= 1000000000)
296 return (EINVAL);
297
36750816 298 return (cond_wait_common(cond, mutex, abstime, 1));
71b3fa15
DX
299}
300
301static int
302cond_signal_common(pthread_cond_t *cond, int broadcast)
303{
9e2ee207 304 struct pthread *curthread = tls_get_curthread();
71b3fa15
DX
305 pthread_cond_t cv;
306 int ret = 0;
307
308 /*
309 * If the condition variable is statically initialized, perform dynamic
310 * initialization.
311 */
312 if (__predict_false(*cond == NULL &&
313 (ret = init_static(curthread, cond)) != 0))
314 return (ret);
315
316 cv = *cond;
317 /* Lock the condition variable structure. */
318 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
319 if (cv->c_waiters) {
320 if (!broadcast) {
321 cv->c_wakeups++;
322 cv->c_waiters--;
323 cv->c_seqno++;
324 _thr_umtx_wake(&cv->c_seqno, 1);
325 } else {
326 cv->c_wakeups += cv->c_waiters;
327 cv->c_waiters = 0;
328 cv->c_seqno++;
329 _thr_umtx_wake(&cv->c_seqno, INT_MAX);
330 }
331 }
332 THR_LOCK_RELEASE(curthread, &cv->c_lock);
333 return (ret);
334}
335
336int
337_pthread_cond_signal(pthread_cond_t * cond)
338{
36750816
DX
339
340 return (cond_signal_common(cond, 0));
71b3fa15
DX
341}
342
343__strong_reference(_pthread_cond_signal, _thr_cond_signal);
344
345int
346_pthread_cond_broadcast(pthread_cond_t * cond)
347{
36750816
DX
348
349 return (cond_signal_common(cond, 1));
71b3fa15
DX
350}
351
352__strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast);