Style fixes.
[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 *
36750816 26 * $DragonFly: src/lib/libthread_xu/thread/thr_cond.c,v 1.2 2005/02/26 02:04:22 davidxu Exp $
71b3fa15
DX
27 */
28
29#include <stdlib.h>
30#include <errno.h>
31#include <string.h>
32#include <pthread.h>
33#include <limits.h>
34
35#include "thr_private.h"
36
37/*
38 * Prototypes
39 */
40static int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
41static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
42 const struct timespec *abstime, int cancel);
43static int cond_signal_common(pthread_cond_t *cond, int broadcast);
44
45/*
46 * Double underscore versions are cancellation points. Single underscore
47 * versions are not and are provided for libc internal usage (which
48 * shouldn't introduce cancellation points).
49 */
50__weak_reference(__pthread_cond_wait, pthread_cond_wait);
51__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
52
53__weak_reference(_pthread_cond_init, pthread_cond_init);
54__weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
55__weak_reference(_pthread_cond_signal, pthread_cond_signal);
56__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
57
58static int
59cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
60{
61 pthread_cond_t pcond;
62 int rval = 0;
63
64 if ((pcond = (pthread_cond_t)
65 malloc(sizeof(struct pthread_cond))) == NULL) {
66 rval = ENOMEM;
67 } else {
68 /*
69 * Initialise the condition variable structure:
70 */
71 _thr_umtx_init(&pcond->c_lock);
72 pcond->c_seqno = 0;
73 pcond->c_waiters = 0;
74 pcond->c_wakeups = 0;
75 if (cond_attr == NULL || *cond_attr == NULL) {
76 pcond->c_pshared = 0;
77 pcond->c_clockid = CLOCK_REALTIME;
78 } else {
79 pcond->c_pshared = (*cond_attr)->c_pshared;
80 pcond->c_clockid = (*cond_attr)->c_clockid;
81 }
82 *cond = pcond;
83 }
84 /* Return the completion status: */
85 return (rval);
86}
87
88static int
89init_static(struct pthread *thread, pthread_cond_t *cond)
90{
91 int ret;
92
93 THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
94
95 if (*cond == NULL)
96 ret = cond_init(cond, NULL);
97 else
98 ret = 0;
99
100 THR_LOCK_RELEASE(thread, &_cond_static_lock);
101
102 return (ret);
103}
104
105int
106_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
107{
108 *cond = NULL;
109 return cond_init(cond, cond_attr);
110}
111
112int
113_pthread_cond_destroy(pthread_cond_t *cond)
114{
115 struct pthread_cond *cv;
116 struct pthread *curthread = _get_curthread();
117 int rval = 0;
118
119 if (*cond == NULL)
120 rval = EINVAL;
121 else {
122 /* Lock the condition variable structure: */
123 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
124 if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) {
125 THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
126 return (EBUSY);
127 }
128
129 /*
130 * NULL the caller's pointer now that the condition
131 * variable has been destroyed:
132 */
133 cv = *cond;
134 *cond = NULL;
135
136 /* Unlock the condition variable structure: */
137 THR_LOCK_RELEASE(curthread, &cv->c_lock);
138
139 /* Free the cond lock structure: */
140
141 /*
142 * Free the memory allocated for the condition
143 * variable structure:
144 */
145 free(cv);
146
147 }
148 /* Return the completion status: */
149 return (rval);
150}
151
152struct cond_cancel_info
153{
154 pthread_mutex_t *mutex;
155 pthread_cond_t *cond;
156 long seqno;
157};
158
159static void
160cond_cancel_handler(void *arg)
161{
162 struct pthread *curthread = _get_curthread();
163 struct cond_cancel_info *cci = (struct cond_cancel_info *)arg;
164 pthread_cond_t cv;
165
166 cv = *(cci->cond);
167 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
168 if (cv->c_seqno != cci->seqno && cv->c_wakeups != 0) {
169 if (cv->c_waiters > 0) {
170 cv->c_seqno++;
171 _thr_umtx_wake(&cv->c_seqno, 1);
172 } else
173 cv->c_wakeups--;
174 } else {
175 cv->c_waiters--;
176 }
177 THR_LOCK_RELEASE(curthread, &cv->c_lock);
178
179 _mutex_cv_lock(cci->mutex);
180}
181
182static int
183cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
184 const struct timespec *abstime, int cancel)
185{
186 struct pthread *curthread = _get_curthread();
187 struct timespec ts, ts2, *tsp;
188 struct cond_cancel_info cci;
189 pthread_cond_t cv;
190 long seq, oldseq;
191 int oldcancel;
192 int ret = 0;
193
194 /*
195 * If the condition variable is statically initialized,
196 * perform the dynamic initialization:
197 */
198 if (__predict_false(*cond == NULL &&
199 (ret = init_static(curthread, cond)) != 0))
200 return (ret);
201
202 cv = *cond;
203 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
204 ret = _mutex_cv_unlock(mutex);
205 if (ret) {
206 THR_LOCK_RELEASE(curthread, &cv->c_lock);
207 return (ret);
208 }
209 oldseq = seq = cv->c_seqno;
210 cci.mutex = mutex;
211 cci.cond = cond;
212 cci.seqno = oldseq;
213
214 cv->c_waiters++;
215 do {
216 THR_LOCK_RELEASE(curthread, &cv->c_lock);
217
218 if (abstime != NULL) {
219 clock_gettime(cv->c_clockid, &ts);
220 TIMESPEC_SUB(&ts2, abstime, &ts);
221 tsp = &ts2;
222 } else
223 tsp = NULL;
224
225 if (cancel) {
226 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &cci);
227 oldcancel = _thr_cancel_enter(curthread);
228 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
229 _thr_cancel_leave(curthread, oldcancel);
230 THR_CLEANUP_POP(curthread, 0);
231 } else {
232 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
233 }
234
235 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
236 seq = cv->c_seqno;
237 if (abstime != NULL && ret == ETIMEDOUT)
238 break;
239
240 /*
241 * loop if we have never been told to wake up
242 * or we lost a race.
243 */
244 } while (seq == oldseq || cv->c_wakeups == 0);
245
246 if (seq != oldseq && cv->c_wakeups != 0) {
247 cv->c_wakeups--;
248 ret = 0;
249 } else {
250 cv->c_waiters--;
251 }
252 THR_LOCK_RELEASE(curthread, &cv->c_lock);
253 _mutex_cv_lock(mutex);
254 return (ret);
255}
256
257int
258_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
259{
36750816
DX
260
261 return (cond_wait_common(cond, mutex, NULL, 0));
71b3fa15
DX
262}
263
264__strong_reference(_pthread_cond_wait, _thr_cond_wait);
265
266int
267__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
268{
71b3fa15 269
36750816 270 return (cond_wait_common(cond, mutex, NULL, 1));
71b3fa15
DX
271}
272
273int
274_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
275 const struct timespec * abstime)
276{
277 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
278 abstime->tv_nsec >= 1000000000)
279 return (EINVAL);
280
36750816 281 return (cond_wait_common(cond, mutex, abstime, 0));
71b3fa15
DX
282}
283
284__strong_reference(_pthread_cond_timedwait, _thr_cond_timedwait);
285
286int
287__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
288 const struct timespec *abstime)
289{
290 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
291 abstime->tv_nsec >= 1000000000)
292 return (EINVAL);
293
36750816 294 return (cond_wait_common(cond, mutex, abstime, 1));
71b3fa15
DX
295}
296
297static int
298cond_signal_common(pthread_cond_t *cond, int broadcast)
299{
300 struct pthread *curthread = _get_curthread();
301 pthread_cond_t cv;
302 int ret = 0;
303
304 /*
305 * If the condition variable is statically initialized, perform dynamic
306 * initialization.
307 */
308 if (__predict_false(*cond == NULL &&
309 (ret = init_static(curthread, cond)) != 0))
310 return (ret);
311
312 cv = *cond;
313 /* Lock the condition variable structure. */
314 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
315 if (cv->c_waiters) {
316 if (!broadcast) {
317 cv->c_wakeups++;
318 cv->c_waiters--;
319 cv->c_seqno++;
320 _thr_umtx_wake(&cv->c_seqno, 1);
321 } else {
322 cv->c_wakeups += cv->c_waiters;
323 cv->c_waiters = 0;
324 cv->c_seqno++;
325 _thr_umtx_wake(&cv->c_seqno, INT_MAX);
326 }
327 }
328 THR_LOCK_RELEASE(curthread, &cv->c_lock);
329 return (ret);
330}
331
332int
333_pthread_cond_signal(pthread_cond_t * cond)
334{
36750816
DX
335
336 return (cond_signal_common(cond, 0));
71b3fa15
DX
337}
338
339__strong_reference(_pthread_cond_signal, _thr_cond_signal);
340
341int
342_pthread_cond_broadcast(pthread_cond_t * cond)
343{
36750816
DX
344
345 return (cond_signal_common(cond, 1));
71b3fa15
DX
346}
347
348__strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast);