libthread_xu - Fix rtld and refactor locks
[dragonfly.git] / lib / libthread_xu / thread / thr_cond.c
blob563d8f854c6ad7ec31c0a90989e01eddd6c19b0e
1 /*
2 * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3 * All rights reserved.
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.
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.
28 #include "namespace.h"
29 #include <machine/tls.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <pthread.h>
33 #include <limits.h>
34 #include "un-namespace.h"
36 #include "thr_private.h"
38 #ifdef _PTHREADS_DEBUGGING
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <sys/file.h>
42 #endif
44 #define cpu_ccfence() __asm __volatile("" : : : "memory")
46 umtx_t _cond_static_lock;
48 #ifdef _PTHREADS_DEBUGGING
50 static
51 void
52 cond_log(const char *ctl, ...)
54 char buf[256];
55 va_list va;
56 size_t len;
58 va_start(va, ctl);
59 len = vsnprintf(buf, sizeof(buf), ctl, va);
60 va_end(va);
61 _thr_log(buf, len);
64 #else
66 static __inline
67 void
68 cond_log(const char *ctl __unused, ...)
72 #endif
75 * Prototypes
77 int __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
78 int __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
79 const struct timespec *abstime);
80 static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
81 const struct timespec *abstime, int cancel);
82 static int cond_signal_common(pthread_cond_t *cond, int broadcast);
84 static int
85 cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
87 pthread_cond_t pcond;
88 int rval = 0;
90 if ((pcond = (pthread_cond_t)
91 malloc(sizeof(struct pthread_cond))) == NULL) {
92 rval = ENOMEM;
93 } else {
95 * Initialise the condition variable structure:
97 _thr_umtx_init(&pcond->c_lock);
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;
105 TAILQ_INIT(&pcond->c_waitlist);
106 *cond = pcond;
108 /* Return the completion status: */
109 return (rval);
112 #if 0
113 void
114 _cond_reinit(pthread_cond_t cond)
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);
126 #endif
128 static int
129 init_static(struct pthread *thread, pthread_cond_t *cond)
131 int ret;
133 THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
135 if (*cond == NULL)
136 ret = cond_init(cond, NULL);
137 else
138 ret = 0;
140 THR_LOCK_RELEASE(thread, &_cond_static_lock);
142 return (ret);
146 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
148 *cond = NULL;
149 return cond_init(cond, cond_attr);
153 _pthread_cond_destroy(pthread_cond_t *cond)
155 struct pthread_cond *cv;
156 struct pthread *curthread = tls_get_curthread();
157 int rval = 0;
159 if (cond == NULL)
160 rval = EINVAL;
161 else if (*cond == NULL)
162 rval = 0;
163 else {
164 /* Lock the condition variable structure: */
165 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
166 if (TAILQ_FIRST(&(*cond)->c_waitlist)) {
167 THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
168 return (EBUSY);
172 * NULL the caller's pointer now that the condition
173 * variable has been destroyed:
175 cv = *cond;
176 *cond = NULL;
178 /* Unlock the condition variable structure: */
179 THR_LOCK_RELEASE(curthread, &cv->c_lock);
181 /* Free the cond lock structure: */
184 * Free the memory allocated for the condition
185 * variable structure:
187 free(cv);
190 /* Return the completion status: */
191 return (rval);
194 struct cond_cancel_info {
195 TAILQ_ENTRY(cond_cancel_info) entry;
196 pthread_mutex_t *mutex;
197 pthread_cond_t *cond;
198 int count;
199 int queued;
202 static void
203 cond_cancel_handler(void *arg)
205 struct pthread *curthread = tls_get_curthread();
206 struct cond_cancel_info *info = (struct cond_cancel_info *)arg;
207 pthread_cond_t cv;
209 cv = *info->cond;
210 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
211 cond_log("cond_cancel %p\n", cv);
213 if (info->queued) {
214 info->queued = 0;
215 cond_log("cond_cancel %p: info %p\n", cv, info);
216 TAILQ_REMOVE(&cv->c_waitlist, info, entry);
217 _thr_umtx_wake(&info->queued, 0);
219 THR_LOCK_RELEASE(curthread, &cv->c_lock);
221 /* _mutex_cv_lock(info->mutex, info->count); */
225 * Wait for pthread_cond_t to be signaled.
227 * NOTE: EINTR is ignored and may not be returned by this function.
229 static int
230 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
231 const struct timespec *abstime, int cancel)
233 struct pthread *curthread = tls_get_curthread();
234 struct timespec ts, ts2, *tsp;
235 struct cond_cancel_info info;
236 pthread_cond_t cv;
237 int oldcancel;
238 int ret;
241 * If the condition variable is statically initialized,
242 * perform the dynamic initialization:
244 cond_log("cond_wait_common %p on mutex %p info %p\n",
245 *cond, *mutex, &info);
246 if (__predict_false(*cond == NULL &&
247 (ret = init_static(curthread, cond)) != 0)) {
248 cond_log("cond_wait_common %p (failedA %d)\n", *cond, ret);
249 return (ret);
252 cv = *cond;
253 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
254 ret = _mutex_cv_unlock(mutex, &info.count);
255 if (ret) {
256 cond_log("cond_wait_common %p (failedB %d)\n", cv, ret);
257 THR_LOCK_RELEASE(curthread, &cv->c_lock);
258 return ret;
261 cpu_ccfence();
262 info.mutex = mutex;
263 info.cond = cond;
264 info.queued = 1;
265 TAILQ_INSERT_TAIL(&cv->c_waitlist, &info, entry);
268 * loop if we have never been told to wake up
269 * or we lost a race.
271 while (info.queued) {
272 THR_LOCK_RELEASE(curthread, &cv->c_lock);
274 if (abstime != NULL) {
275 clock_gettime(cv->c_clockid, &ts);
276 TIMESPEC_SUB(&ts2, abstime, &ts);
277 tsp = &ts2;
278 } else {
279 tsp = NULL;
282 if (cancel) {
283 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info);
284 oldcancel = _thr_cancel_enter(curthread);
285 ret = _thr_umtx_wait(&info.queued, 1, tsp,
286 cv->c_clockid);
287 _thr_cancel_leave(curthread, oldcancel);
288 THR_CLEANUP_POP(curthread, 0);
289 } else {
290 ret = _thr_umtx_wait(&info.queued, 1, tsp,
291 cv->c_clockid);
295 * Ignore EINTR. Make sure ret is 0 if not ETIMEDOUT.
297 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
298 if (abstime != NULL && ret == ETIMEDOUT)
299 break;
300 cpu_ccfence();
303 if (info.queued) {
304 info.queued = 0;
305 TAILQ_REMOVE(&cv->c_waitlist, &info, entry);
306 ret = ETIMEDOUT;
307 } else {
308 ret = 0;
310 THR_LOCK_RELEASE(curthread, &cv->c_lock);
312 cond_log("cond_wait_common %p (doneA)\n", cv);
313 _mutex_cv_lock(mutex, info.count);
315 if (ret)
316 cond_log("cond_wait_common %p (failed %d)\n", cv, ret);
317 else
318 cond_log("cond_wait_common %p (doneB)\n", cv);
320 return (ret);
324 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
326 return (cond_wait_common(cond, mutex, NULL, 0));
330 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
332 return (cond_wait_common(cond, mutex, NULL, 1));
336 _pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
337 const struct timespec * abstime)
339 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
340 abstime->tv_nsec >= 1000000000)
341 return (EINVAL);
343 return (cond_wait_common(cond, mutex, abstime, 0));
347 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
348 const struct timespec *abstime)
350 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
351 abstime->tv_nsec >= 1000000000)
352 return (EINVAL);
354 return (cond_wait_common(cond, mutex, abstime, 1));
357 static int
358 cond_signal_common(pthread_cond_t *cond, int broadcast)
360 struct pthread *curthread = tls_get_curthread();
361 struct cond_cancel_info *info;
362 pthread_cond_t cv;
363 int ret = 0;
365 cond_log("cond_signal_common %p broad=%d\n", *cond, broadcast);
368 * If the condition variable is statically initialized, perform dynamic
369 * initialization.
371 if (__predict_false(*cond == NULL &&
372 (ret = init_static(curthread, cond)) != 0)) {
373 cond_log("cond_signal_common %p (failedA %d)\n", *cond, ret);
374 return (ret);
377 cv = *cond;
378 /* Lock the condition variable structure. */
379 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
380 while ((info = TAILQ_FIRST(&cv->c_waitlist)) != NULL) {
381 info->queued = 0;
382 TAILQ_REMOVE(&cv->c_waitlist, info, entry);
383 cond_log("cond_signal_common %p: wakeup %p\n", *cond, info);
384 _thr_umtx_wake(&info->queued, 0);
385 if (broadcast == 0)
386 break;
388 THR_LOCK_RELEASE(curthread, &cv->c_lock);
390 if (ret)
391 cond_log("cond_signal_common %p (failedB %d)\n", *cond, ret);
392 else
393 cond_log("cond_signal_common %p (done)\n", *cond);
395 return (ret);
399 _pthread_cond_signal(pthread_cond_t * cond)
401 return (cond_signal_common(cond, 0));
405 _pthread_cond_broadcast(pthread_cond_t * cond)
407 return (cond_signal_common(cond, 1));
411 * Double underscore versions are cancellation points. Single underscore
412 * versions are not and are provided for libc internal usage (which
413 * shouldn't introduce cancellation points).
415 __strong_reference(__pthread_cond_wait, pthread_cond_wait);
416 __strong_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
418 __strong_reference(_pthread_cond_init, pthread_cond_init);
419 __strong_reference(_pthread_cond_destroy, pthread_cond_destroy);
420 __strong_reference(_pthread_cond_signal, pthread_cond_signal);
421 __strong_reference(_pthread_cond_broadcast, pthread_cond_broadcast);