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. 13165967Simp * 3. Neither the name of the author nor the names of any co-contributors 1413546Sjulian * may be used to endorse or promote products derived from this software 1513546Sjulian * without specific prior written permission. 1613546Sjulian * 1713546Sjulian * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 1813546Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1913546Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2044963Sjb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2113546Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2213546Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2313546Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2413546Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2513546Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2613546Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2713546Sjulian * SUCH DAMAGE. 2813546Sjulian * 2950476Speter * $FreeBSD$ 3013546Sjulian */ 31174112Sdeischen 32174112Sdeischen#include "namespace.h" 3313546Sjulian#include <stdlib.h> 3413546Sjulian#include <errno.h> 3536830Sjb#include <string.h> 3613546Sjulian#include <pthread.h> 37174112Sdeischen#include "un-namespace.h" 38103388Smini#include "thr_private.h" 3913546Sjulian 40113658Sdeischen#define THR_IN_CONDQ(thr) (((thr)->sflags & THR_FLAGS_IN_SYNCQ) != 0) 41113658Sdeischen#define THR_CONDQ_SET(thr) (thr)->sflags |= THR_FLAGS_IN_SYNCQ 42113658Sdeischen#define THR_CONDQ_CLEAR(thr) (thr)->sflags &= ~THR_FLAGS_IN_SYNCQ 43113658Sdeischen 4444963Sjb/* 4544963Sjb * Prototypes 4644963Sjb */ 47113658Sdeischenstatic inline struct pthread *cond_queue_deq(pthread_cond_t); 48113658Sdeischenstatic inline void cond_queue_remove(pthread_cond_t, pthread_t); 49113658Sdeischenstatic inline void cond_queue_enq(pthread_cond_t, pthread_t); 50139023Sdeischenstatic void cond_wait_backout(void *); 51139023Sdeischenstatic inline void check_continuation(struct pthread *, 52139023Sdeischen struct pthread_cond *, pthread_mutex_t *); 5344963Sjb 54174112Sdeischenint __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 55174112Sdeischenint __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 56174112Sdeischen const struct timespec *abstime); 57174112Sdeischen 58115173Sdeischen/* 59115173Sdeischen * Double underscore versions are cancellation points. Single underscore 60115173Sdeischen * versions are not and are provided for libc internal usage (which 61115173Sdeischen * shouldn't introduce cancellation points). 62115173Sdeischen */ 63115173Sdeischen__weak_reference(__pthread_cond_wait, pthread_cond_wait); 64115173Sdeischen__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 65115173Sdeischen 6675369Sdeischen__weak_reference(_pthread_cond_init, pthread_cond_init); 6775369Sdeischen__weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 6875369Sdeischen__weak_reference(_pthread_cond_signal, pthread_cond_signal); 6975369Sdeischen__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 7071581Sdeischen 7171581Sdeischen 7248046Sjbint 7371581Sdeischen_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 7413546Sjulian{ 7513546Sjulian enum pthread_cond_type type; 7617706Sjulian pthread_cond_t pcond; 77113658Sdeischen int flags; 7813546Sjulian int rval = 0; 7913546Sjulian 8035509Sjb if (cond == NULL) 8131402Salex rval = EINVAL; 8235509Sjb else { 8317706Sjulian /* 8424827Sjb * Check if a pointer to a condition variable attribute 8524827Sjb * structure was passed by the caller: 8617706Sjulian */ 8717706Sjulian if (cond_attr != NULL && *cond_attr != NULL) { 8817706Sjulian /* Default to a fast condition variable: */ 8917706Sjulian type = (*cond_attr)->c_type; 90113658Sdeischen flags = (*cond_attr)->c_flags; 9117706Sjulian } else { 9217706Sjulian /* Default to a fast condition variable: */ 9317706Sjulian type = COND_TYPE_FAST; 94113658Sdeischen flags = 0; 9517706Sjulian } 9613546Sjulian 9717706Sjulian /* Process according to condition variable type: */ 9817706Sjulian switch (type) { 9924827Sjb /* Fast condition variable: */ 10017706Sjulian case COND_TYPE_FAST: 10117706Sjulian /* Nothing to do here. */ 10217706Sjulian break; 10313546Sjulian 10424827Sjb /* Trap invalid condition variable types: */ 10517706Sjulian default: 10617706Sjulian /* Return an invalid argument error: */ 10731402Salex rval = EINVAL; 10817706Sjulian break; 10917706Sjulian } 11013546Sjulian 11117706Sjulian /* Check for no errors: */ 11217706Sjulian if (rval == 0) { 11324827Sjb if ((pcond = (pthread_cond_t) 11424827Sjb malloc(sizeof(struct pthread_cond))) == NULL) { 11531402Salex rval = ENOMEM; 116113658Sdeischen } else if (_lock_init(&pcond->c_lock, LCK_ADAPTIVE, 117173967Sjasone _thr_lock_wait, _thr_lock_wakeup, calloc) != 0) { 118113658Sdeischen free(pcond); 119113658Sdeischen rval = ENOMEM; 12017706Sjulian } else { 12117706Sjulian /* 12217706Sjulian * Initialise the condition variable 12317706Sjulian * structure: 12417706Sjulian */ 12544963Sjb TAILQ_INIT(&pcond->c_queue); 126120069Sdavidxu pcond->c_flags = COND_FLAGS_INITED; 12717706Sjulian pcond->c_type = type; 12844963Sjb pcond->c_mutex = NULL; 12968516Sdeischen pcond->c_seqno = 0; 13017706Sjulian *cond = pcond; 13117706Sjulian } 13217706Sjulian } 13313546Sjulian } 13413546Sjulian /* Return the completion status: */ 13513546Sjulian return (rval); 13613546Sjulian} 13713546Sjulian 13813546Sjulianint 13971581Sdeischen_pthread_cond_destroy(pthread_cond_t *cond) 14013546Sjulian{ 141113658Sdeischen struct pthread_cond *cv; 142113658Sdeischen struct pthread *curthread = _get_curthread(); 143113658Sdeischen int rval = 0; 14413546Sjulian 14535509Sjb if (cond == NULL || *cond == NULL) 14631402Salex rval = EINVAL; 14735509Sjb else { 14835509Sjb /* Lock the condition variable structure: */ 149113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 15013546Sjulian 15135509Sjb /* 152113658Sdeischen * NULL the caller's pointer now that the condition 153113658Sdeischen * variable has been destroyed: 154113658Sdeischen */ 155113658Sdeischen cv = *cond; 156113658Sdeischen *cond = NULL; 157113658Sdeischen 158113658Sdeischen /* Unlock the condition variable structure: */ 159113658Sdeischen THR_LOCK_RELEASE(curthread, &cv->c_lock); 160113658Sdeischen 161115761Sdavidxu /* Free the cond lock structure: */ 162115761Sdavidxu _lock_destroy(&cv->c_lock); 163115761Sdavidxu 164113658Sdeischen /* 16535509Sjb * Free the memory allocated for the condition 16635509Sjb * variable structure: 16735509Sjb */ 168113658Sdeischen free(cv); 16917706Sjulian 17013546Sjulian } 17113546Sjulian /* Return the completion status: */ 17213546Sjulian return (rval); 17313546Sjulian} 17413546Sjulian 17513546Sjulianint 17671581Sdeischen_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 17713546Sjulian{ 17871581Sdeischen struct pthread *curthread = _get_curthread(); 17956277Sjasone int rval = 0; 18068516Sdeischen int done = 0; 181139023Sdeischen int mutex_locked = 1; 18268516Sdeischen int seqno; 18313546Sjulian 184115173Sdeischen if (cond == NULL) 18568516Sdeischen return (EINVAL); 18635027Sjb 18735027Sjb /* 18835027Sjb * If the condition variable is statically initialized, 18935027Sjb * perform the dynamic initialization: 19035027Sjb */ 19168516Sdeischen if (*cond == NULL && 192174112Sdeischen (rval = _pthread_cond_init(cond, NULL)) != 0) 19368516Sdeischen return (rval); 19468516Sdeischen 195115278Sdeischen if (!_kse_isthreaded()) 196115278Sdeischen _kse_setthreaded(1); 197115278Sdeischen 19868516Sdeischen /* 19968516Sdeischen * Enter a loop waiting for a condition signal or broadcast 20068516Sdeischen * to wake up this thread. A loop is needed in case the waiting 20168516Sdeischen * thread is interrupted by a signal to execute a signal handler. 20268516Sdeischen * It is not (currently) possible to remain in the waiting queue 20368516Sdeischen * while running a handler. Instead, the thread is interrupted 20468516Sdeischen * and backed out of the waiting queue prior to executing the 20568516Sdeischen * signal handler. 20668516Sdeischen */ 207139023Sdeischen 208139023Sdeischen /* Lock the condition variable structure: */ 209139023Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 210139023Sdeischen seqno = (*cond)->c_seqno; 21168516Sdeischen do { 21247424Sjb /* 21347424Sjb * If the condvar was statically allocated, properly 21447424Sjb * initialize the tail queue. 21547424Sjb */ 21647424Sjb if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 21747424Sjb TAILQ_INIT(&(*cond)->c_queue); 21847424Sjb (*cond)->c_flags |= COND_FLAGS_INITED; 21947424Sjb } 22047424Sjb 22117706Sjulian /* Process according to condition variable type: */ 22217706Sjulian switch ((*cond)->c_type) { 22324827Sjb /* Fast condition variable: */ 22417706Sjulian case COND_TYPE_FAST: 22544963Sjb if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 22644963Sjb ((*cond)->c_mutex != *mutex))) { 22744963Sjb /* Return invalid argument error: */ 22844963Sjb rval = EINVAL; 22944963Sjb } else { 23053812Salfred /* Reset the timeout and interrupted flags: */ 23171581Sdeischen curthread->timeout = 0; 23271581Sdeischen curthread->interrupted = 0; 23313546Sjulian 23440974Sdt /* 23544963Sjb * Queue the running thread for the condition 23644963Sjb * variable: 23740974Sdt */ 23871581Sdeischen cond_queue_enq(*cond, curthread); 23913546Sjulian 24044963Sjb /* Wait forever: */ 24171581Sdeischen curthread->wakeup_time.tv_sec = -1; 24244963Sjb 24344963Sjb /* Unlock the mutex: */ 244139023Sdeischen if (mutex_locked && 245113658Sdeischen ((rval = _mutex_cv_unlock(mutex)) != 0)) { 24644963Sjb /* 24744963Sjb * Cannot unlock the mutex, so remove 24844963Sjb * the running thread from the condition 24944963Sjb * variable queue: 25044963Sjb */ 25171581Sdeischen cond_queue_remove(*cond, curthread); 252113658Sdeischen } 253113658Sdeischen else { 254139023Sdeischen /* Remember the mutex: */ 255139023Sdeischen (*cond)->c_mutex = *mutex; 256139023Sdeischen 25744963Sjb /* 258113658Sdeischen * Don't unlock the mutex the next 259113658Sdeischen * time through the loop (if the 260113658Sdeischen * thread has to be requeued after 261113658Sdeischen * handling a signal). 26244963Sjb */ 263139023Sdeischen mutex_locked = 0; 26444963Sjb 265113658Sdeischen /* 266113658Sdeischen * This thread is active and is in a 267113658Sdeischen * critical region (holding the cv 268113658Sdeischen * lock); we should be able to safely 269113658Sdeischen * set the state. 270113658Sdeischen */ 271115080Sdeischen THR_SCHED_LOCK(curthread, curthread); 272113658Sdeischen THR_SET_STATE(curthread, PS_COND_WAIT); 27356277Sjasone 274113658Sdeischen /* Remember the CV: */ 275113658Sdeischen curthread->data.cond = *cond; 276139023Sdeischen curthread->sigbackout = cond_wait_backout; 277115080Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 27881918Sjasone 279113658Sdeischen /* Unlock the CV structure: */ 280113658Sdeischen THR_LOCK_RELEASE(curthread, 281113658Sdeischen &(*cond)->c_lock); 282113658Sdeischen 283113658Sdeischen /* Schedule the next thread: */ 284113658Sdeischen _thr_sched_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); 297139023Sdeischen if (done && !THR_IN_CONDQ(curthread)) { 29856277Sjasone /* 299139023Sdeischen * The thread is dequeued, so 300139023Sdeischen * it is safe to clear these. 30153812Salfred */ 302139023Sdeischen curthread->data.cond = NULL; 303139023Sdeischen curthread->sigbackout = NULL; 304139023Sdeischen check_continuation(curthread, 305139023Sdeischen NULL, mutex); 306139023Sdeischen return (_mutex_cv_lock(mutex)); 307139023Sdeischen } 30853812Salfred 309139023Sdeischen /* Relock the CV structure: */ 310139023Sdeischen THR_LOCK_ACQUIRE(curthread, 311139023Sdeischen &(*cond)->c_lock); 312139023Sdeischen 313139023Sdeischen /* 314139023Sdeischen * Clear these after taking the lock to 315139023Sdeischen * prevent a race condition where a 316139023Sdeischen * signal can arrive before dequeueing 317139023Sdeischen * the thread. 318139023Sdeischen */ 319139023Sdeischen curthread->data.cond = NULL; 320139023Sdeischen curthread->sigbackout = NULL; 321139023Sdeischen done = (seqno != (*cond)->c_seqno); 322139023Sdeischen 323139023Sdeischen if (THR_IN_CONDQ(curthread)) { 32453812Salfred cond_queue_remove(*cond, 32571581Sdeischen curthread); 32653812Salfred 32753812Salfred /* Check for no more waiters: */ 328157700Sdelphij if (TAILQ_EMPTY(&(*cond)->c_queue)) 32953812Salfred (*cond)->c_mutex = NULL; 330113658Sdeischen } 33144963Sjb } 33240974Sdt } 33317706Sjulian break; 33413546Sjulian 33524827Sjb /* Trap invalid condition variable types: */ 33617706Sjulian default: 33717706Sjulian /* Return an invalid argument error: */ 33831402Salex rval = EINVAL; 33917706Sjulian break; 34017706Sjulian } 34153812Salfred 342139023Sdeischen check_continuation(curthread, *cond, 343139023Sdeischen mutex_locked ? NULL : mutex); 34468516Sdeischen } while ((done == 0) && (rval == 0)); 34513546Sjulian 346139023Sdeischen /* Unlock the condition variable structure: */ 347139023Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 348139023Sdeischen 349139023Sdeischen if (mutex_locked == 0) 350139023Sdeischen _mutex_cv_lock(mutex); 351139023Sdeischen 35213546Sjulian /* Return the completion status: */ 35313546Sjulian return (rval); 35413546Sjulian} 35513546Sjulian 356115399Skan__strong_reference(_pthread_cond_wait, _thr_cond_wait); 357115399Skan 35813546Sjulianint 359113658Sdeischen__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 360113658Sdeischen{ 361113658Sdeischen struct pthread *curthread = _get_curthread(); 362113658Sdeischen int ret; 363113658Sdeischen 364123312Sdavidxu _thr_cancel_enter(curthread); 365113658Sdeischen ret = _pthread_cond_wait(cond, mutex); 366123312Sdavidxu _thr_cancel_leave(curthread, 1); 367113658Sdeischen return (ret); 368113658Sdeischen} 369113658Sdeischen 370113658Sdeischenint 37171581Sdeischen_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 37213546Sjulian const struct timespec * abstime) 37313546Sjulian{ 37471581Sdeischen struct pthread *curthread = _get_curthread(); 37556277Sjasone int rval = 0; 37668516Sdeischen int done = 0; 377139023Sdeischen int mutex_locked = 1; 37868516Sdeischen int seqno; 37913546Sjulian 380113870Sdeischen THR_ASSERT(curthread->locklevel == 0, 381113870Sdeischen "cv_timedwait: locklevel is not zero!"); 382113658Sdeischen 38363355Sjasone if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 384115173Sdeischen abstime->tv_nsec >= 1000000000) 38568516Sdeischen return (EINVAL); 38635027Sjb /* 38763355Sjasone * If the condition variable is statically initialized, perform dynamic 38863355Sjasone * initialization. 38935027Sjb */ 390174112Sdeischen if (*cond == NULL && (rval = _pthread_cond_init(cond, NULL)) != 0) 39168516Sdeischen return (rval); 39268516Sdeischen 393115278Sdeischen if (!_kse_isthreaded()) 394115278Sdeischen _kse_setthreaded(1); 395115278Sdeischen 39668516Sdeischen /* 39768516Sdeischen * Enter a loop waiting for a condition signal or broadcast 39868516Sdeischen * to wake up this thread. A loop is needed in case the waiting 39968516Sdeischen * thread is interrupted by a signal to execute a signal handler. 40068516Sdeischen * It is not (currently) possible to remain in the waiting queue 40168516Sdeischen * while running a handler. Instead, the thread is interrupted 40268516Sdeischen * and backed out of the waiting queue prior to executing the 40368516Sdeischen * signal handler. 40468516Sdeischen */ 405139023Sdeischen 406139023Sdeischen /* Lock the condition variable structure: */ 407139023Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 408139023Sdeischen seqno = (*cond)->c_seqno; 40968516Sdeischen do { 41047424Sjb /* 41147424Sjb * If the condvar was statically allocated, properly 41247424Sjb * initialize the tail queue. 41347424Sjb */ 41447424Sjb if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 41547424Sjb TAILQ_INIT(&(*cond)->c_queue); 41647424Sjb (*cond)->c_flags |= COND_FLAGS_INITED; 41747424Sjb } 41847424Sjb 41917706Sjulian /* Process according to condition variable type: */ 42017706Sjulian switch ((*cond)->c_type) { 42124827Sjb /* Fast condition variable: */ 42217706Sjulian case COND_TYPE_FAST: 42344963Sjb if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 42444963Sjb ((*cond)->c_mutex != *mutex))) { 42544963Sjb /* Return invalid argument error: */ 42644963Sjb rval = EINVAL; 42744963Sjb } else { 42853812Salfred /* Reset the timeout and interrupted flags: */ 42971581Sdeischen curthread->timeout = 0; 43071581Sdeischen curthread->interrupted = 0; 43113546Sjulian 43217706Sjulian /* 43344963Sjb * Queue the running thread for the condition 43444963Sjb * variable: 43517706Sjulian */ 43671581Sdeischen cond_queue_enq(*cond, curthread); 43740974Sdt 43844963Sjb /* Unlock the mutex: */ 439139023Sdeischen if (mutex_locked && 440113658Sdeischen ((rval = _mutex_cv_unlock(mutex)) != 0)) { 44144963Sjb /* 442113658Sdeischen * Cannot unlock the mutex; remove the 443113658Sdeischen * running thread from the condition 44444963Sjb * variable queue: 44544963Sjb */ 44671581Sdeischen cond_queue_remove(*cond, curthread); 447139023Sdeischen } else { 448139023Sdeischen /* Remember the mutex: */ 449139023Sdeischen (*cond)->c_mutex = *mutex; 45044963Sjb 45144963Sjb /* 452113658Sdeischen * Don't unlock the mutex the next 453113658Sdeischen * time through the loop (if the 454113658Sdeischen * thread has to be requeued after 455113658Sdeischen * handling a signal). 45644963Sjb */ 457139023Sdeischen mutex_locked = 0; 45844963Sjb 459113658Sdeischen /* 460113658Sdeischen * This thread is active and is in a 461113658Sdeischen * critical region (holding the cv 462113658Sdeischen * lock); we should be able to safely 463113658Sdeischen * set the state. 464113658Sdeischen */ 465115080Sdeischen THR_SCHED_LOCK(curthread, curthread); 466155962Sdeischen /* Set the wakeup time: */ 467155962Sdeischen curthread->wakeup_time.tv_sec = 468155962Sdeischen abstime->tv_sec; 469155962Sdeischen curthread->wakeup_time.tv_nsec = 470155962Sdeischen abstime->tv_nsec; 471113658Sdeischen THR_SET_STATE(curthread, PS_COND_WAIT); 47268516Sdeischen 473113658Sdeischen /* Remember the CV: */ 474113658Sdeischen curthread->data.cond = *cond; 475139023Sdeischen curthread->sigbackout = cond_wait_backout; 476115080Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 47781918Sjasone 478113658Sdeischen /* Unlock the CV structure: */ 479113658Sdeischen THR_LOCK_RELEASE(curthread, 480113658Sdeischen &(*cond)->c_lock); 481113658Sdeischen 482113658Sdeischen /* Schedule the next thread: */ 483113658Sdeischen _thr_sched_switch(curthread); 484113658Sdeischen 48553812Salfred /* 486113658Sdeischen * XXX - This really isn't a good check 487113658Sdeischen * since there can be more than one 488113658Sdeischen * thread waiting on the CV. Signals 489113658Sdeischen * sent to threads waiting on mutexes 490113658Sdeischen * or CVs should really be deferred 491113658Sdeischen * until the threads are no longer 492113658Sdeischen * waiting, but POSIX says that signals 493113658Sdeischen * should be sent "as soon as possible". 49453812Salfred */ 495113658Sdeischen done = (seqno != (*cond)->c_seqno); 496139023Sdeischen if (done && !THR_IN_CONDQ(curthread)) { 49781918Sjasone /* 498139023Sdeischen * The thread is dequeued, so 499139023Sdeischen * it is safe to clear these. 50081918Sjasone */ 501139023Sdeischen curthread->data.cond = NULL; 502139023Sdeischen curthread->sigbackout = NULL; 503139023Sdeischen check_continuation(curthread, 504139023Sdeischen NULL, mutex); 505139023Sdeischen return (_mutex_cv_lock(mutex)); 506139023Sdeischen } 50744963Sjb 508139023Sdeischen /* Relock the CV structure: */ 509139023Sdeischen THR_LOCK_ACQUIRE(curthread, 510139023Sdeischen &(*cond)->c_lock); 511139023Sdeischen 512139023Sdeischen /* 513139023Sdeischen * Clear these after taking the lock to 514139023Sdeischen * prevent a race condition where a 515139023Sdeischen * signal can arrive before dequeueing 516139023Sdeischen * the thread. 517139023Sdeischen */ 518139023Sdeischen curthread->data.cond = NULL; 519139023Sdeischen curthread->sigbackout = NULL; 520139023Sdeischen 521139023Sdeischen done = (seqno != (*cond)->c_seqno); 522139023Sdeischen 523139023Sdeischen if (THR_IN_CONDQ(curthread)) { 52444963Sjb cond_queue_remove(*cond, 52571581Sdeischen curthread); 52644963Sjb 52744963Sjb /* Check for no more waiters: */ 528157700Sdelphij if (TAILQ_EMPTY(&(*cond)->c_queue)) 52944963Sjb (*cond)->c_mutex = NULL; 530113658Sdeischen } 53144963Sjb 532113658Sdeischen if (curthread->timeout != 0) { 533113658Sdeischen /* The wait timedout. */ 534113658Sdeischen rval = ETIMEDOUT; 535117165Sdavidxu } 53617706Sjulian } 53713546Sjulian } 53817706Sjulian break; 53917706Sjulian 54024827Sjb /* Trap invalid condition variable types: */ 54117706Sjulian default: 54217706Sjulian /* Return an invalid argument error: */ 54331402Salex rval = EINVAL; 54417706Sjulian break; 54513546Sjulian } 54613546Sjulian 547139023Sdeischen check_continuation(curthread, *cond, 548139023Sdeischen mutex_locked ? NULL : mutex); 54968516Sdeischen } while ((done == 0) && (rval == 0)); 55013546Sjulian 551139023Sdeischen /* Unlock the condition variable structure: */ 552139023Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 553139023Sdeischen 554139023Sdeischen if (mutex_locked == 0) 555139023Sdeischen _mutex_cv_lock(mutex); 556139023Sdeischen 55713546Sjulian /* Return the completion status: */ 55813546Sjulian return (rval); 55913546Sjulian} 56013546Sjulian 56113546Sjulianint 562113658Sdeischen__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 563113658Sdeischen const struct timespec *abstime) 564113658Sdeischen{ 565113658Sdeischen struct pthread *curthread = _get_curthread(); 566113658Sdeischen int ret; 567113658Sdeischen 568123312Sdavidxu _thr_cancel_enter(curthread); 569113658Sdeischen ret = _pthread_cond_timedwait(cond, mutex, abstime); 570123312Sdavidxu _thr_cancel_leave(curthread, 1); 571113658Sdeischen return (ret); 572113658Sdeischen} 573113658Sdeischen 574113658Sdeischen 575113658Sdeischenint 57671581Sdeischen_pthread_cond_signal(pthread_cond_t * cond) 57713546Sjulian{ 578113658Sdeischen struct pthread *curthread = _get_curthread(); 579113658Sdeischen struct pthread *pthread; 580117907Sdeischen struct kse_mailbox *kmbx; 581113658Sdeischen int rval = 0; 58213546Sjulian 583113870Sdeischen THR_ASSERT(curthread->locklevel == 0, 584113870Sdeischen "cv_timedwait: locklevel is not zero!"); 58563355Sjasone if (cond == NULL) 58631402Salex rval = EINVAL; 58763355Sjasone /* 58863355Sjasone * If the condition variable is statically initialized, perform dynamic 58963355Sjasone * initialization. 59063355Sjasone */ 591174112Sdeischen else if (*cond != NULL || (rval = _pthread_cond_init(cond, NULL)) == 0) { 59235509Sjb /* Lock the condition variable structure: */ 593113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 59413546Sjulian 59517706Sjulian /* Process according to condition variable type: */ 59617706Sjulian switch ((*cond)->c_type) { 59724827Sjb /* Fast condition variable: */ 59817706Sjulian case COND_TYPE_FAST: 59968516Sdeischen /* Increment the sequence number: */ 60068516Sdeischen (*cond)->c_seqno++; 60168516Sdeischen 602113658Sdeischen /* 603113658Sdeischen * Wakeups have to be done with the CV lock held; 604113658Sdeischen * otherwise there is a race condition where the 605113658Sdeischen * thread can timeout, run on another KSE, and enter 606113658Sdeischen * another blocking state (including blocking on a CV). 607113658Sdeischen */ 608113658Sdeischen if ((pthread = TAILQ_FIRST(&(*cond)->c_queue)) 609113658Sdeischen != NULL) { 610113658Sdeischen THR_SCHED_LOCK(curthread, pthread); 611113658Sdeischen cond_queue_remove(*cond, pthread); 612139023Sdeischen pthread->sigbackout = NULL; 613117714Sdeischen if ((pthread->kseg == curthread->kseg) && 614117714Sdeischen (pthread->active_priority > 615117714Sdeischen curthread->active_priority)) 616117714Sdeischen curthread->critical_yield = 1; 617117907Sdeischen kmbx = _thr_setrunnable_unlocked(pthread); 618113658Sdeischen THR_SCHED_UNLOCK(curthread, pthread); 619117907Sdeischen if (kmbx != NULL) 620117907Sdeischen kse_wakeup(kmbx); 62161681Sjasone } 62244963Sjb /* Check for no more waiters: */ 623157700Sdelphij if (TAILQ_EMPTY(&(*cond)->c_queue)) 62444963Sjb (*cond)->c_mutex = NULL; 62517706Sjulian break; 62617706Sjulian 62724827Sjb /* Trap invalid condition variable types: */ 62817706Sjulian default: 62917706Sjulian /* Return an invalid argument error: */ 63031402Salex rval = EINVAL; 63117706Sjulian break; 63213546Sjulian } 63313546Sjulian 63435509Sjb /* Unlock the condition variable structure: */ 635113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 63613546Sjulian } 63713546Sjulian 63813546Sjulian /* Return the completion status: */ 63913546Sjulian return (rval); 64013546Sjulian} 64113546Sjulian 642115399Skan__strong_reference(_pthread_cond_signal, _thr_cond_signal); 643115399Skan 64413546Sjulianint 64571581Sdeischen_pthread_cond_broadcast(pthread_cond_t * cond) 64613546Sjulian{ 647113658Sdeischen struct pthread *curthread = _get_curthread(); 648113658Sdeischen struct pthread *pthread; 649117907Sdeischen struct kse_mailbox *kmbx; 650113658Sdeischen int rval = 0; 65113546Sjulian 652113870Sdeischen THR_ASSERT(curthread->locklevel == 0, 653113870Sdeischen "cv_timedwait: locklevel is not zero!"); 65463355Sjasone if (cond == NULL) 65535509Sjb rval = EINVAL; 65663355Sjasone /* 65763355Sjasone * If the condition variable is statically initialized, perform dynamic 65863355Sjasone * initialization. 65963355Sjasone */ 660174112Sdeischen else if (*cond != NULL || (rval = _pthread_cond_init(cond, NULL)) == 0) { 66135509Sjb /* Lock the condition variable structure: */ 662113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 66313546Sjulian 66435509Sjb /* Process according to condition variable type: */ 66535509Sjb switch ((*cond)->c_type) { 66635509Sjb /* Fast condition variable: */ 66735509Sjb case COND_TYPE_FAST: 66868516Sdeischen /* Increment the sequence number: */ 66968516Sdeischen (*cond)->c_seqno++; 67068516Sdeischen 67135509Sjb /* 67235509Sjb * Enter a loop to bring all threads off the 67335509Sjb * condition queue: 67435509Sjb */ 675113658Sdeischen while ((pthread = TAILQ_FIRST(&(*cond)->c_queue)) 676113658Sdeischen != NULL) { 677113658Sdeischen THR_SCHED_LOCK(curthread, pthread); 678113658Sdeischen cond_queue_remove(*cond, pthread); 679139023Sdeischen pthread->sigbackout = NULL; 680117714Sdeischen if ((pthread->kseg == curthread->kseg) && 681117714Sdeischen (pthread->active_priority > 682117714Sdeischen curthread->active_priority)) 683117714Sdeischen curthread->critical_yield = 1; 684117907Sdeischen kmbx = _thr_setrunnable_unlocked(pthread); 685113658Sdeischen THR_SCHED_UNLOCK(curthread, pthread); 686117907Sdeischen if (kmbx != NULL) 687117907Sdeischen kse_wakeup(kmbx); 68835509Sjb } 68944963Sjb 69044963Sjb /* There are no more waiting threads: */ 69144963Sjb (*cond)->c_mutex = NULL; 69235509Sjb break; 693115399Skan 69435509Sjb /* Trap invalid condition variable types: */ 69535509Sjb default: 69635509Sjb /* Return an invalid argument error: */ 69735509Sjb rval = EINVAL; 69835509Sjb break; 69913546Sjulian } 70013546Sjulian 70135509Sjb /* Unlock the condition variable structure: */ 702113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 70313546Sjulian } 70413546Sjulian 70513546Sjulian /* Return the completion status: */ 70613546Sjulian return (rval); 70713546Sjulian} 70844963Sjb 709115399Skan__strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast); 710115399Skan 711139023Sdeischenstatic inline void 712139023Sdeischencheck_continuation(struct pthread *curthread, struct pthread_cond *cond, 713139023Sdeischen pthread_mutex_t *mutex) 71467097Sdeischen{ 715139023Sdeischen if ((curthread->interrupted != 0) && 716139023Sdeischen (curthread->continuation != NULL)) { 717139023Sdeischen if (cond != NULL) 718139023Sdeischen /* Unlock the condition variable structure: */ 719139023Sdeischen THR_LOCK_RELEASE(curthread, &cond->c_lock); 720139023Sdeischen /* 721139023Sdeischen * Note that even though this thread may have been 722139023Sdeischen * canceled, POSIX requires that the mutex be 723139023Sdeischen * reaquired prior to cancellation. 724139023Sdeischen */ 725139023Sdeischen if (mutex != NULL) 726139023Sdeischen _mutex_cv_lock(mutex); 727139023Sdeischen curthread->continuation((void *) curthread); 728139023Sdeischen PANIC("continuation returned in pthread_cond_wait.\n"); 729139023Sdeischen } 730139023Sdeischen} 731139023Sdeischen 732139023Sdeischenstatic void 733139023Sdeischencond_wait_backout(void *arg) 734139023Sdeischen{ 735139023Sdeischen struct pthread *curthread = (struct pthread *)arg; 73667097Sdeischen pthread_cond_t cond; 73767097Sdeischen 738113658Sdeischen cond = curthread->data.cond; 73967097Sdeischen if (cond != NULL) { 74067097Sdeischen /* Lock the condition variable structure: */ 741113658Sdeischen THR_LOCK_ACQUIRE(curthread, &cond->c_lock); 74267097Sdeischen 74367097Sdeischen /* Process according to condition variable type: */ 74467097Sdeischen switch (cond->c_type) { 74567097Sdeischen /* Fast condition variable: */ 74667097Sdeischen case COND_TYPE_FAST: 747113658Sdeischen cond_queue_remove(cond, curthread); 74867097Sdeischen 74967097Sdeischen /* Check for no more waiters: */ 750157700Sdelphij if (TAILQ_EMPTY(&cond->c_queue)) 75167097Sdeischen cond->c_mutex = NULL; 75267097Sdeischen break; 75367097Sdeischen 75467097Sdeischen default: 75567097Sdeischen break; 75667097Sdeischen } 75767097Sdeischen 75867097Sdeischen /* Unlock the condition variable structure: */ 759113658Sdeischen THR_LOCK_RELEASE(curthread, &cond->c_lock); 76067097Sdeischen } 761139023Sdeischen /* No need to call this again. */ 762139023Sdeischen curthread->sigbackout = NULL; 76367097Sdeischen} 76467097Sdeischen 76544963Sjb/* 76644963Sjb * Dequeue a waiting thread from the head of a condition queue in 76744963Sjb * descending priority order. 76844963Sjb */ 769113658Sdeischenstatic inline struct pthread * 77044963Sjbcond_queue_deq(pthread_cond_t cond) 77144963Sjb{ 772113658Sdeischen struct pthread *pthread; 77344963Sjb 77453812Salfred while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) { 77567097Sdeischen TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 776117165Sdavidxu THR_CONDQ_CLEAR(pthread); 77753812Salfred if ((pthread->timeout == 0) && (pthread->interrupted == 0)) 77853812Salfred /* 77953812Salfred * Only exit the loop when we find a thread 78053812Salfred * that hasn't timed out or been canceled; 78153812Salfred * those threads are already running and don't 78253812Salfred * need their run state changed. 78353812Salfred */ 78453812Salfred break; 78544963Sjb } 78644963Sjb 787113658Sdeischen return (pthread); 78844963Sjb} 78944963Sjb 79044963Sjb/* 79144963Sjb * Remove a waiting thread from a condition queue in descending priority 79244963Sjb * order. 79344963Sjb */ 79444963Sjbstatic inline void 795113658Sdeischencond_queue_remove(pthread_cond_t cond, struct pthread *pthread) 79644963Sjb{ 79744963Sjb /* 79844963Sjb * Because pthread_cond_timedwait() can timeout as well 79944963Sjb * as be signaled by another thread, it is necessary to 80044963Sjb * guard against removing the thread from the queue if 80144963Sjb * it isn't in the queue. 80244963Sjb */ 803113658Sdeischen if (THR_IN_CONDQ(pthread)) { 80467097Sdeischen TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 805113658Sdeischen THR_CONDQ_CLEAR(pthread); 80644963Sjb } 80744963Sjb} 80844963Sjb 80944963Sjb/* 81044963Sjb * Enqueue a waiting thread to a condition queue in descending priority 81144963Sjb * order. 81244963Sjb */ 81344963Sjbstatic inline void 814113658Sdeischencond_queue_enq(pthread_cond_t cond, struct pthread *pthread) 81544963Sjb{ 816113658Sdeischen struct pthread *tid = TAILQ_LAST(&cond->c_queue, cond_head); 81744963Sjb 818113658Sdeischen THR_ASSERT(!THR_IN_SYNCQ(pthread), 819113658Sdeischen "cond_queue_enq: thread already queued!"); 82067097Sdeischen 82144963Sjb /* 82244963Sjb * For the common case of all threads having equal priority, 82344963Sjb * we perform a quick check against the priority of the thread 82444963Sjb * at the tail of the queue. 82544963Sjb */ 82644963Sjb if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) 82767097Sdeischen TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe); 82844963Sjb else { 82944963Sjb tid = TAILQ_FIRST(&cond->c_queue); 83044963Sjb while (pthread->active_priority <= tid->active_priority) 83167097Sdeischen tid = TAILQ_NEXT(tid, sqe); 83267097Sdeischen TAILQ_INSERT_BEFORE(tid, pthread, sqe); 83344963Sjb } 834113658Sdeischen THR_CONDQ_SET(pthread); 83567097Sdeischen pthread->data.cond = cond; 83644963Sjb} 837