nrelease - fix/improve livecd
[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 *
71b3fa15
DX
26 */
27
fc71f871 28#include "namespace.h"
9e2ee207 29#include <machine/tls.h>
71b3fa15 30#include <stdlib.h>
71b3fa15
DX
31#include <string.h>
32#include <pthread.h>
33#include <limits.h>
fc71f871 34#include "un-namespace.h"
71b3fa15
DX
35
36#include "thr_private.h"
37
fcaa7a3a
MD
38#ifdef _PTHREADS_DEBUGGING
39#include <stdio.h>
40#include <stdarg.h>
41#include <sys/file.h>
42#endif
43
44#define cpu_ccfence() __asm __volatile("" : : : "memory")
45
e8382b15
DX
46umtx_t _cond_static_lock;
47
fcaa7a3a
MD
48#ifdef _PTHREADS_DEBUGGING
49
50static
51void
52cond_log(const char *ctl, ...)
53{
54 char buf[256];
55 va_list va;
56 size_t len;
57
58 va_start(va, ctl);
59 len = vsnprintf(buf, sizeof(buf), ctl, va);
60 va_end(va);
61 _thr_log(buf, len);
62}
63
64#else
65
66static __inline
67void
68cond_log(const char *ctl __unused, ...)
69{
70}
71
72#endif
73
71b3fa15
DX
74/*
75 * Prototypes
76 */
19451dc5 77int __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
78int __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
79 const struct timespec *abstime);
71b3fa15 80static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
19451dc5 81 const struct timespec *abstime, int cancel);
71b3fa15
DX
82static int cond_signal_common(pthread_cond_t *cond, int broadcast);
83
71b3fa15
DX
84static int
85cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
86{
19451dc5 87 pthread_cond_t pcond;
88 int rval = 0;
71b3fa15 89
cf8046a9 90 pcond = __malloc(sizeof(struct __pthread_cond_s));
e7bf3f77 91 if (pcond == NULL) {
71b3fa15
DX
92 rval = ENOMEM;
93 } else {
94 /*
95 * Initialise the condition variable structure:
96 */
97 _thr_umtx_init(&pcond->c_lock);
71b3fa15
DX
98 if (cond_attr == NULL || *cond_attr == NULL) {
99 pcond->c_pshared = 0;
100 pcond->c_clockid = CLOCK_REALTIME;
101 } else {
102 pcond->c_pshared = (*cond_attr)->c_pshared;
103 pcond->c_clockid = (*cond_attr)->c_clockid;
104 }
fcaa7a3a 105 TAILQ_INIT(&pcond->c_waitlist);
71b3fa15
DX
106 *cond = pcond;
107 }
108 /* Return the completion status: */
109 return (rval);
110}
111
fcaa7a3a
MD
112#if 0
113void
114_cond_reinit(pthread_cond_t cond)
115{
116 if (cond) {
117 _thr_umtx_init(&cond->c_lock);
118#if 0
119 /* retain state */
120 cond->c_pshared = 0;
121 cond->c_clockid = CLOCK_REALTIME;
122#endif
123 TAILQ_INIT(&cond->c_waitlist);
124 }
125}
126#endif
127
71b3fa15 128static int
940be950 129init_static(pthread_t thread, pthread_cond_t *cond)
71b3fa15
DX
130{
131 int ret;
132
133 THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
134
135 if (*cond == NULL)
136 ret = cond_init(cond, NULL);
137 else
138 ret = 0;
139
140 THR_LOCK_RELEASE(thread, &_cond_static_lock);
141
142 return (ret);
143}
144
145int
d33005aa
SW
146_pthread_cond_init(pthread_cond_t * __restrict cond,
147 const pthread_condattr_t * __restrict cond_attr)
71b3fa15
DX
148{
149 *cond = NULL;
150 return cond_init(cond, cond_attr);
151}
152
153int
154_pthread_cond_destroy(pthread_cond_t *cond)
155{
940be950 156 pthread_cond_t cv;
157 pthread_t curthread = tls_get_curthread();
158 int rval = 0;
71b3fa15 159
e7bf3f77 160 if (cond == NULL) {
71b3fa15 161 rval = EINVAL;
e7bf3f77 162 } else if (*cond == NULL) {
146da5fc 163 rval = 0;
e7bf3f77 164 } else {
71b3fa15
DX
165 /* Lock the condition variable structure: */
166 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
fcaa7a3a 167 if (TAILQ_FIRST(&(*cond)->c_waitlist)) {
71b3fa15
DX
168 THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
169 return (EBUSY);
170 }
171
172 /*
173 * NULL the caller's pointer now that the condition
174 * variable has been destroyed:
175 */
176 cv = *cond;
177 *cond = NULL;
178
179 /* Unlock the condition variable structure: */
180 THR_LOCK_RELEASE(curthread, &cv->c_lock);
181
182 /* Free the cond lock structure: */
183
184 /*
185 * Free the memory allocated for the condition
186 * variable structure:
187 */
e7bf3f77 188 __free(cv);
71b3fa15
DX
189
190 }
191 /* Return the completion status: */
192 return (rval);
193}
194
fcaa7a3a
MD
195struct cond_cancel_info {
196 TAILQ_ENTRY(cond_cancel_info) entry;
71b3fa15
DX
197 pthread_mutex_t *mutex;
198 pthread_cond_t *cond;
19451dc5 199 int count;
fcaa7a3a 200 int queued;
71b3fa15
DX
201};
202
203static void
204cond_cancel_handler(void *arg)
205{
940be950 206 pthread_t curthread = tls_get_curthread();
a8851a0f 207 struct cond_cancel_info *info = (struct cond_cancel_info *)arg;
71b3fa15
DX
208 pthread_cond_t cv;
209
fcaa7a3a 210 cv = *info->cond;
71b3fa15 211 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
fcaa7a3a
MD
212 cond_log("cond_cancel %p\n", cv);
213
214 if (info->queued) {
215 info->queued = 0;
216 cond_log("cond_cancel %p: info %p\n", cv, info);
217 TAILQ_REMOVE(&cv->c_waitlist, info, entry);
218 _thr_umtx_wake(&info->queued, 0);
71b3fa15
DX
219 }
220 THR_LOCK_RELEASE(curthread, &cv->c_lock);
221
fcaa7a3a 222 /* _mutex_cv_lock(info->mutex, info->count); */
71b3fa15
DX
223}
224
fcaa7a3a
MD
225/*
226 * Wait for pthread_cond_t to be signaled.
227 *
228 * NOTE: EINTR is ignored and may not be returned by this function.
229 */
71b3fa15
DX
230static int
231cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
fcaa7a3a 232 const struct timespec *abstime, int cancel)
71b3fa15 233{
940be950 234 pthread_t curthread = tls_get_curthread();
71b3fa15 235 struct timespec ts, ts2, *tsp;
a8851a0f 236 struct cond_cancel_info info;
71b3fa15 237 pthread_cond_t cv;
71b3fa15 238 int oldcancel;
fcaa7a3a 239 int ret;
71b3fa15
DX
240
241 /*
242 * If the condition variable is statically initialized,
243 * perform the dynamic initialization:
244 */
fcaa7a3a
MD
245 cond_log("cond_wait_common %p on mutex %p info %p\n",
246 *cond, *mutex, &info);
71b3fa15 247 if (__predict_false(*cond == NULL &&
fcaa7a3a
MD
248 (ret = init_static(curthread, cond)) != 0)) {
249 cond_log("cond_wait_common %p (failedA %d)\n", *cond, ret);
71b3fa15 250 return (ret);
fcaa7a3a 251 }
71b3fa15
DX
252
253 cv = *cond;
254 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
a8851a0f 255 ret = _mutex_cv_unlock(mutex, &info.count);
71b3fa15 256 if (ret) {
fcaa7a3a 257 cond_log("cond_wait_common %p (failedB %d)\n", cv, ret);
71b3fa15 258 THR_LOCK_RELEASE(curthread, &cv->c_lock);
fcaa7a3a 259 return ret;
71b3fa15 260 }
fcaa7a3a
MD
261
262 cpu_ccfence();
a8851a0f
DX
263 info.mutex = mutex;
264 info.cond = cond;
fcaa7a3a
MD
265 info.queued = 1;
266 TAILQ_INSERT_TAIL(&cv->c_waitlist, &info, entry);
3db51647
MD
267
268 /*
269 * loop if we have never been told to wake up
270 * or we lost a race.
271 */
fcaa7a3a 272 while (info.queued) {
71b3fa15
DX
273 THR_LOCK_RELEASE(curthread, &cv->c_lock);
274
275 if (abstime != NULL) {
276 clock_gettime(cv->c_clockid, &ts);
ce96aca2 277 timespecsub(abstime, &ts, &ts2);
71b3fa15 278 tsp = &ts2;
fcaa7a3a 279 } else {
71b3fa15 280 tsp = NULL;
fcaa7a3a 281 }
71b3fa15
DX
282
283 if (cancel) {
a8851a0f 284 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info);
71b3fa15 285 oldcancel = _thr_cancel_enter(curthread);
fcaa7a3a
MD
286 ret = _thr_umtx_wait(&info.queued, 1, tsp,
287 cv->c_clockid);
71b3fa15
DX
288 _thr_cancel_leave(curthread, oldcancel);
289 THR_CLEANUP_POP(curthread, 0);
290 } else {
fcaa7a3a
MD
291 ret = _thr_umtx_wait(&info.queued, 1, tsp,
292 cv->c_clockid);
71b3fa15
DX
293 }
294
fcaa7a3a
MD
295 /*
296 * Ignore EINTR. Make sure ret is 0 if not ETIMEDOUT.
297 */
71b3fa15 298 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
71b3fa15
DX
299 if (abstime != NULL && ret == ETIMEDOUT)
300 break;
fcaa7a3a 301 cpu_ccfence();
71b3fa15 302 }
fcaa7a3a
MD
303
304 if (info.queued) {
305 info.queued = 0;
306 TAILQ_REMOVE(&cv->c_waitlist, &info, entry);
307 ret = ETIMEDOUT;
308 } else {
3db51647 309 ret = 0;
fcaa7a3a 310 }
71b3fa15 311 THR_LOCK_RELEASE(curthread, &cv->c_lock);
fcaa7a3a
MD
312
313 cond_log("cond_wait_common %p (doneA)\n", cv);
a8851a0f 314 _mutex_cv_lock(mutex, info.count);
fcaa7a3a
MD
315
316 if (ret)
317 cond_log("cond_wait_common %p (failed %d)\n", cv, ret);
318 else
319 cond_log("cond_wait_common %p (doneB)\n", cv);
320
71b3fa15
DX
321 return (ret);
322}
323
324int
325_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
326{
36750816 327 return (cond_wait_common(cond, mutex, NULL, 0));
71b3fa15
DX
328}
329
71b3fa15 330int
d33005aa
SW
331__pthread_cond_wait(pthread_cond_t * __restrict cond,
332 pthread_mutex_t * __restrict mutex)
71b3fa15 333{
36750816 334 return (cond_wait_common(cond, mutex, NULL, 1));
71b3fa15
DX
335}
336
337int
d33005aa
SW
338_pthread_cond_timedwait(pthread_cond_t * __restrict cond,
339 pthread_mutex_t * __restrict mutex,
340 const struct timespec * __restrict abstime)
71b3fa15
DX
341{
342 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
343 abstime->tv_nsec >= 1000000000)
344 return (EINVAL);
345
36750816 346 return (cond_wait_common(cond, mutex, abstime, 0));
71b3fa15
DX
347}
348
71b3fa15
DX
349int
350__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
351 const struct timespec *abstime)
352{
353 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
354 abstime->tv_nsec >= 1000000000)
355 return (EINVAL);
356
36750816 357 return (cond_wait_common(cond, mutex, abstime, 1));
71b3fa15
DX
358}
359
360static int
361cond_signal_common(pthread_cond_t *cond, int broadcast)
362{
940be950 363 pthread_t curthread = tls_get_curthread();
fcaa7a3a 364 struct cond_cancel_info *info;
71b3fa15
DX
365 pthread_cond_t cv;
366 int ret = 0;
367
fcaa7a3a
MD
368 cond_log("cond_signal_common %p broad=%d\n", *cond, broadcast);
369
71b3fa15
DX
370 /*
371 * If the condition variable is statically initialized, perform dynamic
372 * initialization.
373 */
374 if (__predict_false(*cond == NULL &&
fcaa7a3a
MD
375 (ret = init_static(curthread, cond)) != 0)) {
376 cond_log("cond_signal_common %p (failedA %d)\n", *cond, ret);
71b3fa15 377 return (ret);
fcaa7a3a 378 }
71b3fa15
DX
379
380 cv = *cond;
381 /* Lock the condition variable structure. */
382 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
fcaa7a3a
MD
383 while ((info = TAILQ_FIRST(&cv->c_waitlist)) != NULL) {
384 info->queued = 0;
385 TAILQ_REMOVE(&cv->c_waitlist, info, entry);
386 cond_log("cond_signal_common %p: wakeup %p\n", *cond, info);
387 _thr_umtx_wake(&info->queued, 0);
388 if (broadcast == 0)
389 break;
71b3fa15
DX
390 }
391 THR_LOCK_RELEASE(curthread, &cv->c_lock);
fcaa7a3a
MD
392
393 if (ret)
394 cond_log("cond_signal_common %p (failedB %d)\n", *cond, ret);
395 else
396 cond_log("cond_signal_common %p (done)\n", *cond);
397
71b3fa15
DX
398 return (ret);
399}
400
401int
402_pthread_cond_signal(pthread_cond_t * cond)
403{
36750816 404 return (cond_signal_common(cond, 0));
71b3fa15
DX
405}
406
71b3fa15
DX
407int
408_pthread_cond_broadcast(pthread_cond_t * cond)
409{
36750816 410 return (cond_signal_common(cond, 1));
71b3fa15 411}
5a1048c8
DX
412
413/*
414 * Double underscore versions are cancellation points. Single underscore
415 * versions are not and are provided for libc internal usage (which
416 * shouldn't introduce cancellation points).
417 */
418__strong_reference(__pthread_cond_wait, pthread_cond_wait);
419__strong_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
420
421__strong_reference(_pthread_cond_init, pthread_cond_init);
422__strong_reference(_pthread_cond_destroy, pthread_cond_destroy);
423__strong_reference(_pthread_cond_signal, pthread_cond_signal);
424__strong_reference(_pthread_cond_broadcast, pthread_cond_broadcast);