thr_cond.c revision 114187
113546Sjulian/* 213546Sjulian * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. 313546Sjulian * All rights reserved. 413546Sjulian * 513546Sjulian * Redistribution and use in source and binary forms, with or without 613546Sjulian * modification, are permitted provided that the following conditions 713546Sjulian * are met: 813546Sjulian * 1. Redistributions of source code must retain the above copyright 913546Sjulian * notice, this list of conditions and the following disclaimer. 1013546Sjulian * 2. Redistributions in binary form must reproduce the above copyright 1113546Sjulian * notice, this list of conditions and the following disclaimer in the 1213546Sjulian * documentation and/or other materials provided with the distribution. 1313546Sjulian * 3. All advertising materials mentioning features or use of this software 1413546Sjulian * must display the following acknowledgement: 1513546Sjulian * This product includes software developed by John Birrell. 1613546Sjulian * 4. Neither the name of the author nor the names of any co-contributors 1713546Sjulian * may be used to endorse or promote products derived from this software 1813546Sjulian * without specific prior written permission. 1913546Sjulian * 2013546Sjulian * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 2113546Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2213546Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2344963Sjb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2413546Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2513546Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2613546Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2713546Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2813546Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2913546Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3013546Sjulian * SUCH DAMAGE. 3113546Sjulian * 3250476Speter * $FreeBSD: head/lib/libkse/thread/thr_cond.c 114187 2003-04-28 23:56:12Z deischen $ 3313546Sjulian */ 3413546Sjulian#include <stdlib.h> 3513546Sjulian#include <errno.h> 3636830Sjb#include <string.h> 3713546Sjulian#include <pthread.h> 38103388Smini#include "thr_private.h" 3913546Sjulian 40113658Sdeischen#define THR_IN_CONDQ(thr) (((thr)->sflags & THR_FLAGS_IN_SYNCQ) != 0) 41113658Sdeischen#define THR_IN_CONDQ(thr) (((thr)->sflags & THR_FLAGS_IN_SYNCQ) != 0) 42113658Sdeischen#define THR_CONDQ_SET(thr) (thr)->sflags |= THR_FLAGS_IN_SYNCQ 43113658Sdeischen#define THR_CONDQ_CLEAR(thr) (thr)->sflags &= ~THR_FLAGS_IN_SYNCQ 44113658Sdeischen 4544963Sjb/* 4644963Sjb * Prototypes 4744963Sjb */ 48113658Sdeischenstatic inline struct pthread *cond_queue_deq(pthread_cond_t); 49113658Sdeischenstatic inline void cond_queue_remove(pthread_cond_t, pthread_t); 50113658Sdeischenstatic inline void cond_queue_enq(pthread_cond_t, pthread_t); 5144963Sjb 5275369Sdeischen__weak_reference(_pthread_cond_init, pthread_cond_init); 5375369Sdeischen__weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 5475369Sdeischen__weak_reference(_pthread_cond_wait, pthread_cond_wait); 5575369Sdeischen__weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait); 5675369Sdeischen__weak_reference(_pthread_cond_signal, pthread_cond_signal); 5775369Sdeischen__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 5871581Sdeischen 5971581Sdeischen 6048046Sjbint 6171581Sdeischen_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 6213546Sjulian{ 6313546Sjulian enum pthread_cond_type type; 6417706Sjulian pthread_cond_t pcond; 65113658Sdeischen int flags; 6613546Sjulian int rval = 0; 6713546Sjulian 6835509Sjb if (cond == NULL) 6931402Salex rval = EINVAL; 7035509Sjb else { 7117706Sjulian /* 7224827Sjb * Check if a pointer to a condition variable attribute 7324827Sjb * structure was passed by the caller: 7417706Sjulian */ 7517706Sjulian if (cond_attr != NULL && *cond_attr != NULL) { 7617706Sjulian /* Default to a fast condition variable: */ 7717706Sjulian type = (*cond_attr)->c_type; 78113658Sdeischen flags = (*cond_attr)->c_flags; 7917706Sjulian } else { 8017706Sjulian /* Default to a fast condition variable: */ 8117706Sjulian type = COND_TYPE_FAST; 82113658Sdeischen flags = 0; 8317706Sjulian } 8413546Sjulian 8517706Sjulian /* Process according to condition variable type: */ 8617706Sjulian switch (type) { 8724827Sjb /* Fast condition variable: */ 8817706Sjulian case COND_TYPE_FAST: 8917706Sjulian /* Nothing to do here. */ 9017706Sjulian break; 9113546Sjulian 9224827Sjb /* Trap invalid condition variable types: */ 9317706Sjulian default: 9417706Sjulian /* Return an invalid argument error: */ 9531402Salex rval = EINVAL; 9617706Sjulian break; 9717706Sjulian } 9813546Sjulian 9917706Sjulian /* Check for no errors: */ 10017706Sjulian if (rval == 0) { 10124827Sjb if ((pcond = (pthread_cond_t) 10224827Sjb malloc(sizeof(struct pthread_cond))) == NULL) { 10331402Salex rval = ENOMEM; 104113658Sdeischen } else if (_lock_init(&pcond->c_lock, LCK_ADAPTIVE, 105113786Sdeischen _thr_lock_wait, _thr_lock_wakeup) != 0) { 106113658Sdeischen free(pcond); 107113658Sdeischen rval = ENOMEM; 10817706Sjulian } else { 10917706Sjulian /* 11017706Sjulian * Initialise the condition variable 11117706Sjulian * structure: 11217706Sjulian */ 11344963Sjb TAILQ_INIT(&pcond->c_queue); 11417706Sjulian pcond->c_flags |= COND_FLAGS_INITED; 11517706Sjulian pcond->c_type = type; 11644963Sjb pcond->c_mutex = NULL; 11768516Sdeischen pcond->c_seqno = 0; 11817706Sjulian *cond = pcond; 11917706Sjulian } 12017706Sjulian } 12113546Sjulian } 12213546Sjulian /* Return the completion status: */ 12313546Sjulian return (rval); 12413546Sjulian} 12513546Sjulian 12613546Sjulianint 12771581Sdeischen_pthread_cond_destroy(pthread_cond_t *cond) 12813546Sjulian{ 129113658Sdeischen struct pthread_cond *cv; 130113658Sdeischen struct pthread *curthread = _get_curthread(); 131113658Sdeischen int rval = 0; 13213546Sjulian 13335509Sjb if (cond == NULL || *cond == NULL) 13431402Salex rval = EINVAL; 13535509Sjb else { 13635509Sjb /* Lock the condition variable structure: */ 137113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 13813546Sjulian 13935509Sjb /* 140113658Sdeischen * NULL the caller's pointer now that the condition 141113658Sdeischen * variable has been destroyed: 142113658Sdeischen */ 143113658Sdeischen cv = *cond; 144113658Sdeischen *cond = NULL; 145113658Sdeischen 146113658Sdeischen /* Unlock the condition variable structure: */ 147113658Sdeischen THR_LOCK_RELEASE(curthread, &cv->c_lock); 148113658Sdeischen 149113658Sdeischen /* 15035509Sjb * Free the memory allocated for the condition 15135509Sjb * variable structure: 15235509Sjb */ 153113658Sdeischen free(cv); 15417706Sjulian 15513546Sjulian } 15613546Sjulian /* Return the completion status: */ 15713546Sjulian return (rval); 15813546Sjulian} 15913546Sjulian 16013546Sjulianint 16171581Sdeischen_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 16213546Sjulian{ 16371581Sdeischen struct pthread *curthread = _get_curthread(); 16456277Sjasone int rval = 0; 16568516Sdeischen int done = 0; 16656277Sjasone int interrupted = 0; 167113658Sdeischen int unlock_mutex = 1; 16868516Sdeischen int seqno; 16913546Sjulian 170113658Sdeischen _thr_enter_cancellation_point(curthread); 171113658Sdeischen 172113658Sdeischen if (cond == NULL) { 173113658Sdeischen _thr_leave_cancellation_point(curthread); 17468516Sdeischen return (EINVAL); 175113658Sdeischen } 17635027Sjb 17735027Sjb /* 17835027Sjb * If the condition variable is statically initialized, 17935027Sjb * perform the dynamic initialization: 18035027Sjb */ 18168516Sdeischen if (*cond == NULL && 182113658Sdeischen (rval = pthread_cond_init(cond, NULL)) != 0) { 183113658Sdeischen _thr_leave_cancellation_point(curthread); 18468516Sdeischen return (rval); 185113658Sdeischen } 18668516Sdeischen 18768516Sdeischen /* 18868516Sdeischen * Enter a loop waiting for a condition signal or broadcast 18968516Sdeischen * to wake up this thread. A loop is needed in case the waiting 19068516Sdeischen * thread is interrupted by a signal to execute a signal handler. 19168516Sdeischen * It is not (currently) possible to remain in the waiting queue 19268516Sdeischen * while running a handler. Instead, the thread is interrupted 19368516Sdeischen * and backed out of the waiting queue prior to executing the 19468516Sdeischen * signal handler. 19568516Sdeischen */ 19668516Sdeischen do { 19748046Sjb /* Lock the condition variable structure: */ 198113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 19948046Sjb 20047424Sjb /* 20147424Sjb * If the condvar was statically allocated, properly 20247424Sjb * initialize the tail queue. 20347424Sjb */ 20447424Sjb if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 20547424Sjb TAILQ_INIT(&(*cond)->c_queue); 20647424Sjb (*cond)->c_flags |= COND_FLAGS_INITED; 20747424Sjb } 20847424Sjb 20917706Sjulian /* Process according to condition variable type: */ 21017706Sjulian switch ((*cond)->c_type) { 21124827Sjb /* Fast condition variable: */ 21217706Sjulian case COND_TYPE_FAST: 21344963Sjb if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 21444963Sjb ((*cond)->c_mutex != *mutex))) { 21544963Sjb /* Unlock the condition variable structure: */ 216113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 21740974Sdt 21844963Sjb /* Return invalid argument error: */ 21944963Sjb rval = EINVAL; 22044963Sjb } else { 22153812Salfred /* Reset the timeout and interrupted flags: */ 22271581Sdeischen curthread->timeout = 0; 22371581Sdeischen curthread->interrupted = 0; 22413546Sjulian 22540974Sdt /* 22644963Sjb * Queue the running thread for the condition 22744963Sjb * variable: 22840974Sdt */ 22971581Sdeischen cond_queue_enq(*cond, curthread); 23013546Sjulian 23168516Sdeischen /* Remember the mutex and sequence number: */ 23244963Sjb (*cond)->c_mutex = *mutex; 23368516Sdeischen seqno = (*cond)->c_seqno; 23435509Sjb 23544963Sjb /* Wait forever: */ 23671581Sdeischen curthread->wakeup_time.tv_sec = -1; 23744963Sjb 23844963Sjb /* Unlock the mutex: */ 239113658Sdeischen if ((unlock_mutex != 0) && 240113658Sdeischen ((rval = _mutex_cv_unlock(mutex)) != 0)) { 24144963Sjb /* 24244963Sjb * Cannot unlock the mutex, so remove 24344963Sjb * the running thread from the condition 24444963Sjb * variable queue: 24544963Sjb */ 24671581Sdeischen cond_queue_remove(*cond, curthread); 24744963Sjb 24844963Sjb /* Check for no more waiters: */ 249113658Sdeischen if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 25044963Sjb (*cond)->c_mutex = NULL; 25144963Sjb 25244963Sjb /* Unlock the condition variable structure: */ 253113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 254113658Sdeischen } 255113658Sdeischen else { 25644963Sjb /* 257113658Sdeischen * Don't unlock the mutex the next 258113658Sdeischen * time through the loop (if the 259113658Sdeischen * thread has to be requeued after 260113658Sdeischen * handling a signal). 26144963Sjb */ 262113658Sdeischen unlock_mutex = 0; 26344963Sjb 264113658Sdeischen /* 265113658Sdeischen * This thread is active and is in a 266113658Sdeischen * critical region (holding the cv 267113658Sdeischen * lock); we should be able to safely 268113658Sdeischen * set the state. 269113658Sdeischen */ 270114187Sdeischen THR_LOCK_SWITCH(curthread); 271113658Sdeischen THR_SET_STATE(curthread, PS_COND_WAIT); 27256277Sjasone 273113658Sdeischen /* Remember the CV: */ 274113658Sdeischen curthread->data.cond = *cond; 27581918Sjasone 276113658Sdeischen /* Unlock the CV structure: */ 277113658Sdeischen THR_LOCK_RELEASE(curthread, 278113658Sdeischen &(*cond)->c_lock); 279113658Sdeischen 280113658Sdeischen /* Schedule the next thread: */ 281113658Sdeischen _thr_sched_switch(curthread); 282113658Sdeischen 283113658Sdeischen curthread->data.cond = NULL; 284114187Sdeischen THR_UNLOCK_SWITCH(curthread); 285113658Sdeischen 28681918Sjasone /* 287113658Sdeischen * XXX - This really isn't a good check 288113658Sdeischen * since there can be more than one 289113658Sdeischen * thread waiting on the CV. Signals 290113658Sdeischen * sent to threads waiting on mutexes 291113658Sdeischen * or CVs should really be deferred 292113658Sdeischen * until the threads are no longer 293113658Sdeischen * waiting, but POSIX says that signals 294113658Sdeischen * should be sent "as soon as possible". 29581918Sjasone */ 296113658Sdeischen done = (seqno != (*cond)->c_seqno); 297113658Sdeischen 298113658Sdeischen if (THR_IN_SYNCQ(curthread)) { 29956277Sjasone /* 30053812Salfred * Lock the condition variable 30153812Salfred * while removing the thread. 30253812Salfred */ 303113658Sdeischen THR_LOCK_ACQUIRE(curthread, 304113658Sdeischen &(*cond)->c_lock); 30553812Salfred 30653812Salfred cond_queue_remove(*cond, 30771581Sdeischen curthread); 30853812Salfred 30953812Salfred /* Check for no more waiters: */ 31053812Salfred if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 31153812Salfred (*cond)->c_mutex = NULL; 31253812Salfred 313113658Sdeischen THR_LOCK_RELEASE(curthread, 314113658Sdeischen &(*cond)->c_lock); 315113658Sdeischen } 31681918Sjasone 317113658Sdeischen /* 318113658Sdeischen * Save the interrupted flag; locking 319113658Sdeischen * the mutex may destroy it. 320113658Sdeischen */ 321113658Sdeischen interrupted = curthread->interrupted; 322113658Sdeischen 323113658Sdeischen /* 324113658Sdeischen * Note that even though this thread may 325113658Sdeischen * have been canceled, POSIX requires 326113658Sdeischen * that the mutex be reaquired prior to 327113658Sdeischen * cancellation. 328113658Sdeischen */ 329114187Sdeischen if (done != 0) { 33081918Sjasone rval = _mutex_cv_lock(mutex); 331114187Sdeischen unlock_mutex = 1; 332114187Sdeischen } 33344963Sjb } 33440974Sdt } 33517706Sjulian break; 33613546Sjulian 33724827Sjb /* Trap invalid condition variable types: */ 33817706Sjulian default: 33940974Sdt /* Unlock the condition variable structure: */ 340113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 34140974Sdt 34217706Sjulian /* Return an invalid argument error: */ 34331402Salex rval = EINVAL; 34417706Sjulian break; 34517706Sjulian } 34653812Salfred 34771581Sdeischen if ((interrupted != 0) && (curthread->continuation != NULL)) 34871581Sdeischen curthread->continuation((void *) curthread); 34968516Sdeischen } while ((done == 0) && (rval == 0)); 35013546Sjulian 351113658Sdeischen _thr_leave_cancellation_point(curthread); 35256698Sjasone 35313546Sjulian /* Return the completion status: */ 35413546Sjulian return (rval); 35513546Sjulian} 35613546Sjulian 35713546Sjulianint 358113658Sdeischen__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 359113658Sdeischen{ 360113658Sdeischen struct pthread *curthread = _get_curthread(); 361113658Sdeischen int ret; 362113658Sdeischen 363113658Sdeischen _thr_enter_cancellation_point(curthread); 364113658Sdeischen ret = _pthread_cond_wait(cond, mutex); 365113658Sdeischen _thr_leave_cancellation_point(curthread); 366113658Sdeischen return (ret); 367113658Sdeischen} 368113658Sdeischen 369113658Sdeischenint 37071581Sdeischen_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 37113546Sjulian const struct timespec * abstime) 37213546Sjulian{ 37371581Sdeischen struct pthread *curthread = _get_curthread(); 37456277Sjasone int rval = 0; 37568516Sdeischen int done = 0; 37656277Sjasone int interrupted = 0; 377113658Sdeischen int unlock_mutex = 1; 37868516Sdeischen int seqno; 37913546Sjulian 380113870Sdeischen THR_ASSERT(curthread->locklevel == 0, 381113870Sdeischen "cv_timedwait: locklevel is not zero!"); 382113658Sdeischen _thr_enter_cancellation_point(curthread); 383113658Sdeischen 38463355Sjasone if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 385113658Sdeischen abstime->tv_nsec >= 1000000000) { 386113658Sdeischen _thr_leave_cancellation_point(curthread); 38768516Sdeischen return (EINVAL); 388113658Sdeischen } 38935027Sjb /* 39063355Sjasone * If the condition variable is statically initialized, perform dynamic 39163355Sjasone * initialization. 39235027Sjb */ 393113658Sdeischen if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0) { 394113658Sdeischen _thr_leave_cancellation_point(curthread); 39568516Sdeischen return (rval); 396113658Sdeischen } 39768516Sdeischen 39868516Sdeischen /* 39968516Sdeischen * Enter a loop waiting for a condition signal or broadcast 40068516Sdeischen * to wake up this thread. A loop is needed in case the waiting 40168516Sdeischen * thread is interrupted by a signal to execute a signal handler. 40268516Sdeischen * It is not (currently) possible to remain in the waiting queue 40368516Sdeischen * while running a handler. Instead, the thread is interrupted 40468516Sdeischen * and backed out of the waiting queue prior to executing the 40568516Sdeischen * signal handler. 40668516Sdeischen */ 40768516Sdeischen do { 40848046Sjb /* Lock the condition variable structure: */ 409113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 41048046Sjb 41147424Sjb /* 41247424Sjb * If the condvar was statically allocated, properly 41347424Sjb * initialize the tail queue. 41447424Sjb */ 41547424Sjb if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 41647424Sjb TAILQ_INIT(&(*cond)->c_queue); 41747424Sjb (*cond)->c_flags |= COND_FLAGS_INITED; 41847424Sjb } 41947424Sjb 42017706Sjulian /* Process according to condition variable type: */ 42117706Sjulian switch ((*cond)->c_type) { 42224827Sjb /* Fast condition variable: */ 42317706Sjulian case COND_TYPE_FAST: 42444963Sjb if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 42544963Sjb ((*cond)->c_mutex != *mutex))) { 42644963Sjb /* Return invalid argument error: */ 42744963Sjb rval = EINVAL; 42813546Sjulian 42944963Sjb /* Unlock the condition variable structure: */ 430113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 43144963Sjb } else { 43244963Sjb /* Set the wakeup time: */ 433113658Sdeischen curthread->wakeup_time.tv_sec = abstime->tv_sec; 43471581Sdeischen curthread->wakeup_time.tv_nsec = 43544963Sjb abstime->tv_nsec; 43613546Sjulian 43753812Salfred /* Reset the timeout and interrupted flags: */ 43871581Sdeischen curthread->timeout = 0; 43971581Sdeischen curthread->interrupted = 0; 44013546Sjulian 44117706Sjulian /* 44244963Sjb * Queue the running thread for the condition 44344963Sjb * variable: 44417706Sjulian */ 44571581Sdeischen cond_queue_enq(*cond, curthread); 44640974Sdt 44768516Sdeischen /* Remember the mutex and sequence number: */ 44844963Sjb (*cond)->c_mutex = *mutex; 44968516Sdeischen seqno = (*cond)->c_seqno; 45013546Sjulian 45144963Sjb /* Unlock the mutex: */ 452113658Sdeischen if ((unlock_mutex != 0) && 453113658Sdeischen ((rval = _mutex_cv_unlock(mutex)) != 0)) { 45444963Sjb /* 455113658Sdeischen * Cannot unlock the mutex; remove the 456113658Sdeischen * running thread from the condition 45744963Sjb * variable queue: 45844963Sjb */ 45971581Sdeischen cond_queue_remove(*cond, curthread); 46044963Sjb 46144963Sjb /* Check for no more waiters: */ 46244963Sjb if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 46344963Sjb (*cond)->c_mutex = NULL; 46444963Sjb 46544963Sjb /* Unlock the condition variable structure: */ 466113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 46744963Sjb } else { 46844963Sjb /* 469113658Sdeischen * Don't unlock the mutex the next 470113658Sdeischen * time through the loop (if the 471113658Sdeischen * thread has to be requeued after 472113658Sdeischen * handling a signal). 47344963Sjb */ 474113658Sdeischen unlock_mutex = 0; 47544963Sjb 476113658Sdeischen /* 477113658Sdeischen * This thread is active and is in a 478113658Sdeischen * critical region (holding the cv 479113658Sdeischen * lock); we should be able to safely 480113658Sdeischen * set the state. 481113658Sdeischen */ 482114187Sdeischen THR_LOCK_SWITCH(curthread); 483113658Sdeischen THR_SET_STATE(curthread, PS_COND_WAIT); 48468516Sdeischen 485113658Sdeischen /* Remember the CV: */ 486113658Sdeischen curthread->data.cond = *cond; 48781918Sjasone 488113658Sdeischen /* Unlock the CV structure: */ 489113658Sdeischen THR_LOCK_RELEASE(curthread, 490113658Sdeischen &(*cond)->c_lock); 491113658Sdeischen 492113658Sdeischen /* Schedule the next thread: */ 493113658Sdeischen _thr_sched_switch(curthread); 494113658Sdeischen 495113658Sdeischen curthread->data.cond = NULL; 496114187Sdeischen THR_UNLOCK_SWITCH(curthread); 497113658Sdeischen 49853812Salfred /* 499113658Sdeischen * XXX - This really isn't a good check 500113658Sdeischen * since there can be more than one 501113658Sdeischen * thread waiting on the CV. Signals 502113658Sdeischen * sent to threads waiting on mutexes 503113658Sdeischen * or CVs should really be deferred 504113658Sdeischen * until the threads are no longer 505113658Sdeischen * waiting, but POSIX says that signals 506113658Sdeischen * should be sent "as soon as possible". 50753812Salfred */ 508113658Sdeischen done = (seqno != (*cond)->c_seqno); 509113658Sdeischen 510113658Sdeischen if (THR_IN_CONDQ(curthread)) { 51181918Sjasone /* 51281918Sjasone * Lock the condition variable 51381918Sjasone * while removing the thread. 51481918Sjasone */ 515113658Sdeischen THR_LOCK_ACQUIRE(curthread, 516113658Sdeischen &(*cond)->c_lock); 51744963Sjb 51844963Sjb cond_queue_remove(*cond, 51971581Sdeischen curthread); 52044963Sjb 52144963Sjb /* Check for no more waiters: */ 52244963Sjb if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 52344963Sjb (*cond)->c_mutex = NULL; 52444963Sjb 525113658Sdeischen THR_LOCK_RELEASE(curthread, 526113658Sdeischen &(*cond)->c_lock); 527113658Sdeischen } 52844963Sjb 529113658Sdeischen /* 530113658Sdeischen * Save the interrupted flag; locking 531113658Sdeischen * the mutex may destroy it. 532113658Sdeischen */ 533113658Sdeischen interrupted = curthread->interrupted; 534113658Sdeischen if (curthread->timeout != 0) { 535113658Sdeischen /* The wait timedout. */ 536113658Sdeischen rval = ETIMEDOUT; 537113658Sdeischen (void)_mutex_cv_lock(mutex); 538113658Sdeischen } else if ((interrupted == 0) || 539113658Sdeischen (done != 0)) 54081918Sjasone rval = _mutex_cv_lock(mutex); 54117706Sjulian } 54213546Sjulian } 54317706Sjulian break; 54417706Sjulian 54524827Sjb /* Trap invalid condition variable types: */ 54617706Sjulian default: 54740974Sdt /* Unlock the condition variable structure: */ 548113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 54940974Sdt 55017706Sjulian /* Return an invalid argument error: */ 55131402Salex rval = EINVAL; 55217706Sjulian break; 55313546Sjulian } 55413546Sjulian 55571581Sdeischen if ((interrupted != 0) && (curthread->continuation != NULL)) 556113658Sdeischen curthread->continuation((void *)curthread); 55768516Sdeischen } while ((done == 0) && (rval == 0)); 55813546Sjulian 559113658Sdeischen _thr_leave_cancellation_point(curthread); 56063364Sjasone 56113546Sjulian /* Return the completion status: */ 56213546Sjulian return (rval); 56313546Sjulian} 56413546Sjulian 56513546Sjulianint 566113658Sdeischen__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 567113658Sdeischen const struct timespec *abstime) 568113658Sdeischen{ 569113658Sdeischen struct pthread *curthread = _get_curthread(); 570113658Sdeischen int ret; 571113658Sdeischen 572113658Sdeischen _thr_enter_cancellation_point(curthread); 573113658Sdeischen ret = _pthread_cond_timedwait(cond, mutex, abstime); 574113658Sdeischen _thr_leave_cancellation_point(curthread); 575113658Sdeischen return (ret); 576113658Sdeischen} 577113658Sdeischen 578113658Sdeischen 579113658Sdeischenint 58071581Sdeischen_pthread_cond_signal(pthread_cond_t * cond) 58113546Sjulian{ 582113658Sdeischen struct pthread *curthread = _get_curthread(); 583113658Sdeischen struct pthread *pthread; 584113658Sdeischen int rval = 0; 58513546Sjulian 586113870Sdeischen THR_ASSERT(curthread->locklevel == 0, 587113870Sdeischen "cv_timedwait: locklevel is not zero!"); 58863355Sjasone if (cond == NULL) 58931402Salex rval = EINVAL; 59063355Sjasone /* 59163355Sjasone * If the condition variable is statically initialized, perform dynamic 59263355Sjasone * initialization. 59363355Sjasone */ 59468844Sdeischen else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) { 59535509Sjb /* Lock the condition variable structure: */ 596113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 59713546Sjulian 59817706Sjulian /* Process according to condition variable type: */ 59917706Sjulian switch ((*cond)->c_type) { 60024827Sjb /* Fast condition variable: */ 60117706Sjulian case COND_TYPE_FAST: 60268516Sdeischen /* Increment the sequence number: */ 60368516Sdeischen (*cond)->c_seqno++; 60468516Sdeischen 605113658Sdeischen /* 606113658Sdeischen * Wakeups have to be done with the CV lock held; 607113658Sdeischen * otherwise there is a race condition where the 608113658Sdeischen * thread can timeout, run on another KSE, and enter 609113658Sdeischen * another blocking state (including blocking on a CV). 610113658Sdeischen */ 611113658Sdeischen if ((pthread = TAILQ_FIRST(&(*cond)->c_queue)) 612113658Sdeischen != NULL) { 613113658Sdeischen THR_SCHED_LOCK(curthread, pthread); 614113658Sdeischen cond_queue_remove(*cond, pthread); 615113658Sdeischen _thr_setrunnable_unlocked(pthread); 616113658Sdeischen THR_SCHED_UNLOCK(curthread, pthread); 61761681Sjasone } 61844963Sjb /* Check for no more waiters: */ 61944963Sjb if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 62044963Sjb (*cond)->c_mutex = NULL; 62117706Sjulian break; 62217706Sjulian 62324827Sjb /* Trap invalid condition variable types: */ 62417706Sjulian default: 62517706Sjulian /* Return an invalid argument error: */ 62631402Salex rval = EINVAL; 62717706Sjulian break; 62813546Sjulian } 62913546Sjulian 63035509Sjb /* Unlock the condition variable structure: */ 631113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 63213546Sjulian } 63313546Sjulian 63413546Sjulian /* Return the completion status: */ 63513546Sjulian return (rval); 63613546Sjulian} 63713546Sjulian 63813546Sjulianint 63971581Sdeischen_pthread_cond_broadcast(pthread_cond_t * cond) 64013546Sjulian{ 641113658Sdeischen struct pthread *curthread = _get_curthread(); 642113658Sdeischen struct pthread *pthread; 643113658Sdeischen int rval = 0; 64413546Sjulian 645113870Sdeischen THR_ASSERT(curthread->locklevel == 0, 646113870Sdeischen "cv_timedwait: locklevel is not zero!"); 64763355Sjasone if (cond == NULL) 64835509Sjb rval = EINVAL; 64963355Sjasone /* 65063355Sjasone * If the condition variable is statically initialized, perform dynamic 65163355Sjasone * initialization. 65263355Sjasone */ 65368844Sdeischen else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) { 65435509Sjb /* Lock the condition variable structure: */ 655113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 65613546Sjulian 65735509Sjb /* Process according to condition variable type: */ 65835509Sjb switch ((*cond)->c_type) { 65935509Sjb /* Fast condition variable: */ 66035509Sjb case COND_TYPE_FAST: 66168516Sdeischen /* Increment the sequence number: */ 66268516Sdeischen (*cond)->c_seqno++; 66368516Sdeischen 66435509Sjb /* 66535509Sjb * Enter a loop to bring all threads off the 66635509Sjb * condition queue: 66735509Sjb */ 668113658Sdeischen while ((pthread = TAILQ_FIRST(&(*cond)->c_queue)) 669113658Sdeischen != NULL) { 670113658Sdeischen THR_SCHED_LOCK(curthread, pthread); 671113658Sdeischen cond_queue_remove(*cond, pthread); 672113658Sdeischen _thr_setrunnable_unlocked(pthread); 673113658Sdeischen THR_SCHED_UNLOCK(curthread, pthread); 67435509Sjb } 67544963Sjb 67644963Sjb /* There are no more waiting threads: */ 67744963Sjb (*cond)->c_mutex = NULL; 67835509Sjb break; 67935509Sjb 68035509Sjb /* Trap invalid condition variable types: */ 68135509Sjb default: 68235509Sjb /* Return an invalid argument error: */ 68335509Sjb rval = EINVAL; 68435509Sjb break; 68513546Sjulian } 68613546Sjulian 68735509Sjb /* Unlock the condition variable structure: */ 688113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 68913546Sjulian } 69013546Sjulian 69113546Sjulian /* Return the completion status: */ 69213546Sjulian return (rval); 69313546Sjulian} 69444963Sjb 69567097Sdeischenvoid 696113658Sdeischen_cond_wait_backout(struct pthread *curthread) 69767097Sdeischen{ 69867097Sdeischen pthread_cond_t cond; 69967097Sdeischen 700113658Sdeischen cond = curthread->data.cond; 70167097Sdeischen if (cond != NULL) { 70267097Sdeischen /* Lock the condition variable structure: */ 703113658Sdeischen THR_LOCK_ACQUIRE(curthread, &cond->c_lock); 70467097Sdeischen 70567097Sdeischen /* Process according to condition variable type: */ 70667097Sdeischen switch (cond->c_type) { 70767097Sdeischen /* Fast condition variable: */ 70867097Sdeischen case COND_TYPE_FAST: 709113658Sdeischen cond_queue_remove(cond, curthread); 71067097Sdeischen 71167097Sdeischen /* Check for no more waiters: */ 71267097Sdeischen if (TAILQ_FIRST(&cond->c_queue) == NULL) 71367097Sdeischen cond->c_mutex = NULL; 71467097Sdeischen break; 71567097Sdeischen 71667097Sdeischen default: 71767097Sdeischen break; 71867097Sdeischen } 71967097Sdeischen 72067097Sdeischen /* Unlock the condition variable structure: */ 721113658Sdeischen THR_LOCK_RELEASE(curthread, &cond->c_lock); 72267097Sdeischen } 72367097Sdeischen} 72467097Sdeischen 72544963Sjb/* 72644963Sjb * Dequeue a waiting thread from the head of a condition queue in 72744963Sjb * descending priority order. 72844963Sjb */ 729113658Sdeischenstatic inline struct pthread * 73044963Sjbcond_queue_deq(pthread_cond_t cond) 73144963Sjb{ 732113658Sdeischen struct pthread *pthread; 73344963Sjb 73453812Salfred while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) { 73567097Sdeischen TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 736113658Sdeischen THR_CONDQ_SET(pthread); 73753812Salfred if ((pthread->timeout == 0) && (pthread->interrupted == 0)) 73853812Salfred /* 73953812Salfred * Only exit the loop when we find a thread 74053812Salfred * that hasn't timed out or been canceled; 74153812Salfred * those threads are already running and don't 74253812Salfred * need their run state changed. 74353812Salfred */ 74453812Salfred break; 74544963Sjb } 74644963Sjb 747113658Sdeischen return (pthread); 74844963Sjb} 74944963Sjb 75044963Sjb/* 75144963Sjb * Remove a waiting thread from a condition queue in descending priority 75244963Sjb * order. 75344963Sjb */ 75444963Sjbstatic inline void 755113658Sdeischencond_queue_remove(pthread_cond_t cond, struct pthread *pthread) 75644963Sjb{ 75744963Sjb /* 75844963Sjb * Because pthread_cond_timedwait() can timeout as well 75944963Sjb * as be signaled by another thread, it is necessary to 76044963Sjb * guard against removing the thread from the queue if 76144963Sjb * it isn't in the queue. 76244963Sjb */ 763113658Sdeischen if (THR_IN_CONDQ(pthread)) { 76467097Sdeischen TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 765113658Sdeischen THR_CONDQ_CLEAR(pthread); 76644963Sjb } 76744963Sjb} 76844963Sjb 76944963Sjb/* 77044963Sjb * Enqueue a waiting thread to a condition queue in descending priority 77144963Sjb * order. 77244963Sjb */ 77344963Sjbstatic inline void 774113658Sdeischencond_queue_enq(pthread_cond_t cond, struct pthread *pthread) 77544963Sjb{ 776113658Sdeischen struct pthread *tid = TAILQ_LAST(&cond->c_queue, cond_head); 77744963Sjb 778113658Sdeischen THR_ASSERT(!THR_IN_SYNCQ(pthread), 779113658Sdeischen "cond_queue_enq: thread already queued!"); 78067097Sdeischen 78144963Sjb /* 78244963Sjb * For the common case of all threads having equal priority, 78344963Sjb * we perform a quick check against the priority of the thread 78444963Sjb * at the tail of the queue. 78544963Sjb */ 78644963Sjb if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) 78767097Sdeischen TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe); 78844963Sjb else { 78944963Sjb tid = TAILQ_FIRST(&cond->c_queue); 79044963Sjb while (pthread->active_priority <= tid->active_priority) 79167097Sdeischen tid = TAILQ_NEXT(tid, sqe); 79267097Sdeischen TAILQ_INSERT_BEFORE(tid, pthread, sqe); 79344963Sjb } 794113658Sdeischen THR_CONDQ_SET(pthread); 79567097Sdeischen pthread->data.cond = cond; 79644963Sjb} 797