2 * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by John Birrell.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 #include "pthread_private.h"
44 static inline pthread_t cond_queue_deq(pthread_cond_t);
45 static inline void cond_queue_remove(pthread_cond_t, pthread_t);
46 static inline void cond_queue_enq(pthread_cond_t, pthread_t);
48 /* Reinitialize a condition variable to defaults. */
50 _cond_reinit(pthread_cond_t * cond)
56 else if (*cond == NULL)
57 ret = pthread_cond_init(cond, NULL);
60 * Initialize the condition variable structure:
62 TAILQ_INIT(&(*cond)->c_queue);
63 (*cond)->c_flags = COND_FLAGS_INITED;
64 (*cond)->c_type = COND_TYPE_FAST;
65 (*cond)->c_mutex = NULL;
66 memset(&(*cond)->lock, 0, sizeof((*cond)->lock));
72 pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
74 enum pthread_cond_type type;
82 * Check if a pointer to a condition variable attribute
83 * structure was passed by the caller:
85 if (cond_attr != NULL && *cond_attr != NULL) {
86 /* Default to a fast condition variable: */
87 type = (*cond_attr)->c_type;
89 /* Default to a fast condition variable: */
90 type = COND_TYPE_FAST;
93 /* Process according to condition variable type: */
95 /* Fast condition variable: */
97 /* Nothing to do here. */
100 /* Trap invalid condition variable types: */
102 /* Return an invalid argument error: */
107 /* Check for no errors: */
109 if ((pcond = (pthread_cond_t)
110 malloc(sizeof(struct pthread_cond))) == NULL) {
114 * Initialise the condition variable
117 TAILQ_INIT(&pcond->c_queue);
118 pcond->c_flags |= COND_FLAGS_INITED;
119 pcond->c_type = type;
120 pcond->c_mutex = NULL;
121 memset(&pcond->lock,0,sizeof(pcond->lock));
126 /* Return the completion status: */
131 pthread_cond_destroy(pthread_cond_t * cond)
135 if (cond == NULL || *cond == NULL)
138 /* Lock the condition variable structure: */
139 _SPINLOCK(&(*cond)->lock);
142 * Free the memory allocated for the condition
143 * variable structure:
148 * NULL the caller's pointer now that the condition
149 * variable has been destroyed:
153 /* Return the completion status: */
158 pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
166 * If the condition variable is statically initialized,
167 * perform the dynamic initialization:
169 else if (*cond != NULL ||
170 (rval = pthread_cond_init(cond,NULL)) == 0) {
172 _thread_enter_cancellation_point();
174 /* Lock the condition variable structure: */
175 _SPINLOCK(&(*cond)->lock);
178 * If the condvar was statically allocated, properly
179 * initialize the tail queue.
181 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
182 TAILQ_INIT(&(*cond)->c_queue);
183 (*cond)->c_flags |= COND_FLAGS_INITED;
186 /* Process according to condition variable type: */
187 switch ((*cond)->c_type) {
188 /* Fast condition variable: */
190 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
191 ((*cond)->c_mutex != *mutex))) {
192 /* Unlock the condition variable structure: */
193 _SPINUNLOCK(&(*cond)->lock);
195 /* Return invalid argument error: */
198 /* Reset the timeout and interrupted flags: */
199 _thread_run->timeout = 0;
200 _thread_run->interrupted = 0;
203 * Queue the running thread for the condition
206 cond_queue_enq(*cond, _thread_run);
208 /* Remember the mutex that is being used: */
209 (*cond)->c_mutex = *mutex;
212 _thread_run->wakeup_time.tv_sec = -1;
214 /* Unlock the mutex: */
215 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
217 * Cannot unlock the mutex, so remove
218 * the running thread from the condition
221 cond_queue_remove(*cond, _thread_run);
223 /* Check for no more waiters: */
224 if (TAILQ_FIRST(&(*cond)->c_queue) ==
226 (*cond)->c_mutex = NULL;
228 /* Unlock the condition variable structure: */
229 _SPINUNLOCK(&(*cond)->lock);
233 * Schedule the next thread and unlock
234 * the condition variable structure:
236 _thread_kern_sched_state_unlock(PS_COND_WAIT,
237 &(*cond)->lock, __FILE__, __LINE__);
239 if (_thread_run->interrupted != 0) {
241 * Lock the condition variable
242 * while removing the thread.
244 _SPINLOCK(&(*cond)->lock);
246 cond_queue_remove(*cond,
249 /* Check for no more waiters: */
250 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
251 (*cond)->c_mutex = NULL;
253 _SPINUNLOCK(&(*cond)->lock);
257 * Note that even though this thread may have
258 * been canceled, POSIX requires that the mutex
259 * be reaquired prior to cancellation.
261 rval = _mutex_cv_lock(mutex);
266 /* Trap invalid condition variable types: */
268 /* Unlock the condition variable structure: */
269 _SPINUNLOCK(&(*cond)->lock);
271 /* Return an invalid argument error: */
276 if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
277 _thread_exit_cleanup();
278 pthread_exit(PTHREAD_CANCELED);
281 _thread_leave_cancellation_point();
284 /* Return the completion status: */
289 pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
290 const struct timespec * abstime)
294 if (cond == NULL || abstime == NULL)
297 if (abstime->tv_sec < 0 ||
298 abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
304 * If the condition variable is statically initialized,
305 * perform the dynamic initialization:
308 (rval = pthread_cond_init(cond,NULL)) == 0) {
310 _thread_enter_cancellation_point();
312 /* Lock the condition variable structure: */
313 _SPINLOCK(&(*cond)->lock);
316 * If the condvar was statically allocated, properly
317 * initialize the tail queue.
319 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
320 TAILQ_INIT(&(*cond)->c_queue);
321 (*cond)->c_flags |= COND_FLAGS_INITED;
324 /* Process according to condition variable type: */
325 switch ((*cond)->c_type) {
326 /* Fast condition variable: */
328 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
329 ((*cond)->c_mutex != *mutex))) {
330 /* Return invalid argument error: */
333 /* Unlock the condition variable structure: */
334 _SPINUNLOCK(&(*cond)->lock);
336 /* Set the wakeup time: */
337 _thread_run->wakeup_time.tv_sec =
339 _thread_run->wakeup_time.tv_nsec =
342 /* Reset the timeout and interrupted flags: */
343 _thread_run->timeout = 0;
344 _thread_run->interrupted = 0;
347 * Queue the running thread for the condition
350 cond_queue_enq(*cond, _thread_run);
352 /* Remember the mutex that is being used: */
353 (*cond)->c_mutex = *mutex;
355 /* Unlock the mutex: */
356 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
358 * Cannot unlock the mutex, so remove
359 * the running thread from the condition
362 cond_queue_remove(*cond, _thread_run);
364 /* Check for no more waiters: */
365 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
366 (*cond)->c_mutex = NULL;
368 /* Unlock the condition variable structure: */
369 _SPINUNLOCK(&(*cond)->lock);
372 * Schedule the next thread and unlock
373 * the condition variable structure:
375 _thread_kern_sched_state_unlock(PS_COND_WAIT,
376 &(*cond)->lock, __FILE__, __LINE__);
379 * Check if the wait timedout or was
380 * interrupted (canceled):
382 if ((_thread_run->timeout == 0) &&
383 (_thread_run->interrupted == 0)) {
384 /* Lock the mutex: */
385 rval = _mutex_cv_lock(mutex);
388 /* Lock the condition variable structure: */
389 _SPINLOCK(&(*cond)->lock);
392 * The wait timed out; remove
393 * the thread from the condition
396 cond_queue_remove(*cond,
399 /* Check for no more waiters: */
400 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
401 (*cond)->c_mutex = NULL;
403 /* Unock the condition variable structure: */
404 _SPINUNLOCK(&(*cond)->lock);
406 /* Return a timeout error: */
410 * Lock the mutex and ignore any
411 * errors. Note that even though
412 * this thread may have been
413 * canceled, POSIX requires that
414 * the mutex be reaquired prior
417 (void)_mutex_cv_lock(mutex);
423 /* Trap invalid condition variable types: */
425 /* Unlock the condition variable structure: */
426 _SPINUNLOCK(&(*cond)->lock);
428 /* Return an invalid argument error: */
433 if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
434 _thread_exit_cleanup();
435 pthread_exit(PTHREAD_CANCELED);
438 _thread_leave_cancellation_point();
441 /* Return the completion status: */
446 pthread_cond_signal(pthread_cond_t * cond)
451 if (cond == NULL || *cond == NULL)
455 * Defer signals to protect the scheduling queues
456 * from access by the signal handler:
458 _thread_kern_sig_defer();
460 /* Lock the condition variable structure: */
461 _SPINLOCK(&(*cond)->lock);
463 /* Process according to condition variable type: */
464 switch ((*cond)->c_type) {
465 /* Fast condition variable: */
467 if ((pthread = cond_queue_deq(*cond)) != NULL)
468 /* Allow the thread to run: */
469 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
471 /* Check for no more waiters: */
472 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
473 (*cond)->c_mutex = NULL;
476 /* Trap invalid condition variable types: */
478 /* Return an invalid argument error: */
483 /* Unlock the condition variable structure: */
484 _SPINUNLOCK(&(*cond)->lock);
487 * Undefer and handle pending signals, yielding if
490 _thread_kern_sig_undefer();
493 /* Return the completion status: */
498 pthread_cond_broadcast(pthread_cond_t * cond)
503 if (cond == NULL || *cond == NULL)
507 * Defer signals to protect the scheduling queues
508 * from access by the signal handler:
510 _thread_kern_sig_defer();
512 /* Lock the condition variable structure: */
513 _SPINLOCK(&(*cond)->lock);
515 /* Process according to condition variable type: */
516 switch ((*cond)->c_type) {
517 /* Fast condition variable: */
520 * Enter a loop to bring all threads off the
523 while ((pthread = cond_queue_deq(*cond)) != NULL) {
524 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
527 /* There are no more waiting threads: */
528 (*cond)->c_mutex = NULL;
531 /* Trap invalid condition variable types: */
533 /* Return an invalid argument error: */
538 /* Unlock the condition variable structure: */
539 _SPINUNLOCK(&(*cond)->lock);
542 * Undefer and handle pending signals, yielding if
545 _thread_kern_sig_undefer();
548 /* Return the completion status: */
553 * Dequeue a waiting thread from the head of a condition queue in
554 * descending priority order.
556 static inline pthread_t
557 cond_queue_deq(pthread_cond_t cond)
561 while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
562 TAILQ_REMOVE(&cond->c_queue, pthread, qe);
563 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
564 if ((pthread->timeout == 0) && (pthread->interrupted == 0))
566 * Only exit the loop when we find a thread
567 * that hasn't timed out or been canceled;
568 * those threads are already running and don't
569 * need their run state changed.
578 * Remove a waiting thread from a condition queue in descending priority
582 cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
585 * Because pthread_cond_timedwait() can timeout as well
586 * as be signaled by another thread, it is necessary to
587 * guard against removing the thread from the queue if
588 * it isn't in the queue.
590 if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
591 TAILQ_REMOVE(&cond->c_queue, pthread, qe);
592 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
597 * Enqueue a waiting thread to a condition queue in descending priority
601 cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
603 pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
606 * For the common case of all threads having equal priority,
607 * we perform a quick check against the priority of the thread
608 * at the tail of the queue.
610 if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
611 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe);
613 tid = TAILQ_FIRST(&cond->c_queue);
614 while (pthread->active_priority <= tid->active_priority)
615 tid = TAILQ_NEXT(tid, qe);
616 TAILQ_INSERT_BEFORE(tid, pthread, qe);
618 pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;