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
32 * $FreeBSD: src/lib/libc_r/uthread/uthread_cond.c,v 1.22.2.8 2002/10/22 14:44:02 fjoe Exp $
33 * $DragonFly: src/lib/libc_r/uthread/uthread_cond.c,v 1.2 2003/06/17 04:26:48 dillon Exp $
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 __weak_reference(_pthread_cond_init, pthread_cond_init);
49 __weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
50 __weak_reference(_pthread_cond_wait, pthread_cond_wait);
51 __weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait);
52 __weak_reference(_pthread_cond_signal, pthread_cond_signal);
53 __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
56 /* Reinitialize a condition variable to defaults. */
58 _cond_reinit(pthread_cond_t *cond)
64 else if (*cond == NULL)
65 ret = pthread_cond_init(cond, NULL);
68 * Initialize the condition variable structure:
70 TAILQ_INIT(&(*cond)->c_queue);
71 (*cond)->c_flags = COND_FLAGS_INITED;
72 (*cond)->c_type = COND_TYPE_FAST;
73 (*cond)->c_mutex = NULL;
75 memset(&(*cond)->lock, 0, sizeof((*cond)->lock));
81 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
83 enum pthread_cond_type type;
91 * Check if a pointer to a condition variable attribute
92 * structure was passed by the caller:
94 if (cond_attr != NULL && *cond_attr != NULL) {
95 /* Default to a fast condition variable: */
96 type = (*cond_attr)->c_type;
98 /* Default to a fast condition variable: */
99 type = COND_TYPE_FAST;
102 /* Process according to condition variable type: */
104 /* Fast condition variable: */
106 /* Nothing to do here. */
109 /* Trap invalid condition variable types: */
111 /* Return an invalid argument error: */
116 /* Check for no errors: */
118 if ((pcond = (pthread_cond_t)
119 malloc(sizeof(struct pthread_cond))) == NULL) {
123 * Initialise the condition variable
126 TAILQ_INIT(&pcond->c_queue);
127 pcond->c_flags |= COND_FLAGS_INITED;
128 pcond->c_type = type;
129 pcond->c_mutex = NULL;
131 memset(&pcond->lock,0,sizeof(pcond->lock));
136 /* Return the completion status: */
141 _pthread_cond_destroy(pthread_cond_t *cond)
145 if (cond == NULL || *cond == NULL)
148 /* Lock the condition variable structure: */
149 _SPINLOCK(&(*cond)->lock);
152 * Free the memory allocated for the condition
153 * variable structure:
158 * NULL the caller's pointer now that the condition
159 * variable has been destroyed:
163 /* Return the completion status: */
168 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
170 struct pthread *curthread = _get_curthread();
176 _thread_enter_cancellation_point();
182 * If the condition variable is statically initialized,
183 * perform the dynamic initialization:
186 (rval = pthread_cond_init(cond, NULL)) != 0)
190 * Enter a loop waiting for a condition signal or broadcast
191 * to wake up this thread. A loop is needed in case the waiting
192 * thread is interrupted by a signal to execute a signal handler.
193 * It is not (currently) possible to remain in the waiting queue
194 * while running a handler. Instead, the thread is interrupted
195 * and backed out of the waiting queue prior to executing the
199 /* Lock the condition variable structure: */
200 _SPINLOCK(&(*cond)->lock);
203 * If the condvar was statically allocated, properly
204 * initialize the tail queue.
206 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
207 TAILQ_INIT(&(*cond)->c_queue);
208 (*cond)->c_flags |= COND_FLAGS_INITED;
211 /* Process according to condition variable type: */
212 switch ((*cond)->c_type) {
213 /* Fast condition variable: */
215 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
216 ((*cond)->c_mutex != *mutex))) {
217 /* Unlock the condition variable structure: */
218 _SPINUNLOCK(&(*cond)->lock);
220 /* Return invalid argument error: */
223 /* Reset the timeout and interrupted flags: */
224 curthread->timeout = 0;
225 curthread->interrupted = 0;
228 * Queue the running thread for the condition
231 cond_queue_enq(*cond, curthread);
233 /* Remember the mutex and sequence number: */
234 (*cond)->c_mutex = *mutex;
235 seqno = (*cond)->c_seqno;
238 curthread->wakeup_time.tv_sec = -1;
240 /* Unlock the mutex: */
241 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
243 * Cannot unlock the mutex, so remove
244 * the running thread from the condition
247 cond_queue_remove(*cond, curthread);
249 /* Check for no more waiters: */
250 if (TAILQ_FIRST(&(*cond)->c_queue) ==
252 (*cond)->c_mutex = NULL;
254 /* Unlock the condition variable structure: */
255 _SPINUNLOCK(&(*cond)->lock);
258 * Schedule the next thread and unlock
259 * the condition variable structure:
261 _thread_kern_sched_state_unlock(PS_COND_WAIT,
262 &(*cond)->lock, __FILE__, __LINE__);
264 done = (seqno != (*cond)->c_seqno);
266 interrupted = curthread->interrupted;
269 * Check if the wait was interrupted
270 * (canceled) or needs to be resumed
271 * after handling a signal.
273 if (interrupted != 0) {
275 * Lock the mutex and ignore any
276 * errors. Note that even
277 * though this thread may have
278 * been canceled, POSIX requires
279 * that the mutex be reaquired
280 * prior to cancellation.
282 (void)_mutex_cv_lock(mutex);
285 * Lock the condition variable
286 * while removing the thread.
288 _SPINLOCK(&(*cond)->lock);
290 cond_queue_remove(*cond,
293 /* Check for no more waiters: */
294 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
295 (*cond)->c_mutex = NULL;
297 _SPINUNLOCK(&(*cond)->lock);
299 /* Lock the mutex: */
300 rval = _mutex_cv_lock(mutex);
306 /* Trap invalid condition variable types: */
308 /* Unlock the condition variable structure: */
309 _SPINUNLOCK(&(*cond)->lock);
311 /* Return an invalid argument error: */
316 if ((interrupted != 0) && (curthread->continuation != NULL))
317 curthread->continuation((void *) curthread);
318 } while ((done == 0) && (rval == 0));
320 _thread_leave_cancellation_point();
322 /* Return the completion status: */
327 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
328 const struct timespec * abstime)
330 struct pthread *curthread = _get_curthread();
336 _thread_enter_cancellation_point();
338 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
339 abstime->tv_nsec >= 1000000000)
342 * If the condition variable is statically initialized, perform dynamic
345 if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
349 * Enter a loop waiting for a condition signal or broadcast
350 * to wake up this thread. A loop is needed in case the waiting
351 * thread is interrupted by a signal to execute a signal handler.
352 * It is not (currently) possible to remain in the waiting queue
353 * while running a handler. Instead, the thread is interrupted
354 * and backed out of the waiting queue prior to executing the
358 /* Lock the condition variable structure: */
359 _SPINLOCK(&(*cond)->lock);
362 * If the condvar was statically allocated, properly
363 * initialize the tail queue.
365 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
366 TAILQ_INIT(&(*cond)->c_queue);
367 (*cond)->c_flags |= COND_FLAGS_INITED;
370 /* Process according to condition variable type: */
371 switch ((*cond)->c_type) {
372 /* Fast condition variable: */
374 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
375 ((*cond)->c_mutex != *mutex))) {
376 /* Return invalid argument error: */
379 /* Unlock the condition variable structure: */
380 _SPINUNLOCK(&(*cond)->lock);
382 /* Set the wakeup time: */
383 curthread->wakeup_time.tv_sec =
385 curthread->wakeup_time.tv_nsec =
388 /* Reset the timeout and interrupted flags: */
389 curthread->timeout = 0;
390 curthread->interrupted = 0;
393 * Queue the running thread for the condition
396 cond_queue_enq(*cond, curthread);
398 /* Remember the mutex and sequence number: */
399 (*cond)->c_mutex = *mutex;
400 seqno = (*cond)->c_seqno;
402 /* Unlock the mutex: */
403 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
405 * Cannot unlock the mutex, so remove
406 * the running thread from the condition
409 cond_queue_remove(*cond, curthread);
411 /* Check for no more waiters: */
412 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
413 (*cond)->c_mutex = NULL;
415 /* Unlock the condition variable structure: */
416 _SPINUNLOCK(&(*cond)->lock);
419 * Schedule the next thread and unlock
420 * the condition variable structure:
422 _thread_kern_sched_state_unlock(PS_COND_WAIT,
423 &(*cond)->lock, __FILE__, __LINE__);
425 done = (seqno != (*cond)->c_seqno);
427 interrupted = curthread->interrupted;
430 * Check if the wait was interrupted
431 * (canceled) or needs to be resumed
432 * after handling a signal.
434 if (interrupted != 0) {
436 * Lock the mutex and ignore any
437 * errors. Note that even
438 * though this thread may have
439 * been canceled, POSIX requires
440 * that the mutex be reaquired
441 * prior to cancellation.
443 (void)_mutex_cv_lock(mutex);
446 * Lock the condition variable
447 * while removing the thread.
449 _SPINLOCK(&(*cond)->lock);
451 cond_queue_remove(*cond,
454 /* Check for no more waiters: */
455 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
456 (*cond)->c_mutex = NULL;
458 _SPINUNLOCK(&(*cond)->lock);
460 /* Lock the mutex: */
461 rval = _mutex_cv_lock(mutex);
464 * Return ETIMEDOUT if the wait
465 * timed out and there wasn't an
466 * error locking the mutex:
468 if ((curthread->timeout != 0)
476 /* Trap invalid condition variable types: */
478 /* Unlock the condition variable structure: */
479 _SPINUNLOCK(&(*cond)->lock);
481 /* Return an invalid argument error: */
486 if ((interrupted != 0) && (curthread->continuation != NULL))
487 curthread->continuation((void *) curthread);
488 } while ((done == 0) && (rval == 0));
490 _thread_leave_cancellation_point();
492 /* Return the completion status: */
497 _pthread_cond_signal(pthread_cond_t * cond)
505 * If the condition variable is statically initialized, perform dynamic
508 else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
510 * Defer signals to protect the scheduling queues
511 * from access by the signal handler:
513 _thread_kern_sig_defer();
515 /* Lock the condition variable structure: */
516 _SPINLOCK(&(*cond)->lock);
518 /* Process according to condition variable type: */
519 switch ((*cond)->c_type) {
520 /* Fast condition variable: */
522 /* Increment the sequence number: */
525 if ((pthread = cond_queue_deq(*cond)) != NULL) {
527 * Wake up the signaled thread:
529 PTHREAD_NEW_STATE(pthread, PS_RUNNING);
532 /* Check for no more waiters: */
533 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
534 (*cond)->c_mutex = NULL;
537 /* Trap invalid condition variable types: */
539 /* Return an invalid argument error: */
544 /* Unlock the condition variable structure: */
545 _SPINUNLOCK(&(*cond)->lock);
548 * Undefer and handle pending signals, yielding if
551 _thread_kern_sig_undefer();
554 /* Return the completion status: */
559 _pthread_cond_broadcast(pthread_cond_t * cond)
567 * If the condition variable is statically initialized, perform dynamic
570 else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
572 * Defer signals to protect the scheduling queues
573 * from access by the signal handler:
575 _thread_kern_sig_defer();
577 /* Lock the condition variable structure: */
578 _SPINLOCK(&(*cond)->lock);
580 /* Process according to condition variable type: */
581 switch ((*cond)->c_type) {
582 /* Fast condition variable: */
584 /* Increment the sequence number: */
588 * Enter a loop to bring all threads off the
591 while ((pthread = cond_queue_deq(*cond)) != NULL) {
593 * Wake up the signaled thread:
595 PTHREAD_NEW_STATE(pthread, PS_RUNNING);
598 /* There are no more waiting threads: */
599 (*cond)->c_mutex = NULL;
602 /* Trap invalid condition variable types: */
604 /* Return an invalid argument error: */
609 /* Unlock the condition variable structure: */
610 _SPINUNLOCK(&(*cond)->lock);
613 * Undefer and handle pending signals, yielding if
616 _thread_kern_sig_undefer();
619 /* Return the completion status: */
624 _cond_wait_backout(pthread_t pthread)
628 cond = pthread->data.cond;
631 * Defer signals to protect the scheduling queues
632 * from access by the signal handler:
634 _thread_kern_sig_defer();
636 /* Lock the condition variable structure: */
637 _SPINLOCK(&cond->lock);
639 /* Process according to condition variable type: */
640 switch (cond->c_type) {
641 /* Fast condition variable: */
643 cond_queue_remove(cond, pthread);
645 /* Check for no more waiters: */
646 if (TAILQ_FIRST(&cond->c_queue) == NULL)
647 cond->c_mutex = NULL;
654 /* Unlock the condition variable structure: */
655 _SPINUNLOCK(&cond->lock);
658 * Undefer and handle pending signals, yielding if
661 _thread_kern_sig_undefer();
666 * Dequeue a waiting thread from the head of a condition queue in
667 * descending priority order.
669 static inline pthread_t
670 cond_queue_deq(pthread_cond_t cond)
674 while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
675 TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
676 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
677 if ((pthread->timeout == 0) && (pthread->interrupted == 0))
679 * Only exit the loop when we find a thread
680 * that hasn't timed out or been canceled;
681 * those threads are already running and don't
682 * need their run state changed.
691 * Remove a waiting thread from a condition queue in descending priority
695 cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
698 * Because pthread_cond_timedwait() can timeout as well
699 * as be signaled by another thread, it is necessary to
700 * guard against removing the thread from the queue if
701 * it isn't in the queue.
703 if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
704 TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
705 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
710 * Enqueue a waiting thread to a condition queue in descending priority
714 cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
716 pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
718 PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
721 * For the common case of all threads having equal priority,
722 * we perform a quick check against the priority of the thread
723 * at the tail of the queue.
725 if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
726 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
728 tid = TAILQ_FIRST(&cond->c_queue);
729 while (pthread->active_priority <= tid->active_priority)
730 tid = TAILQ_NEXT(tid, sqe);
731 TAILQ_INSERT_BEFORE(tid, pthread, sqe);
733 pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
734 pthread->data.cond = cond;