thr_cond.c revision 120069
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 120069 2003-09-14 22:33:32Z davidxu $ 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_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); 5044963Sjb 51115173Sdeischen/* 52115173Sdeischen * Double underscore versions are cancellation points. Single underscore 53115173Sdeischen * versions are not and are provided for libc internal usage (which 54115173Sdeischen * shouldn't introduce cancellation points). 55115173Sdeischen */ 56115173Sdeischen__weak_reference(__pthread_cond_wait, pthread_cond_wait); 57115173Sdeischen__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 58115173Sdeischen 5975369Sdeischen__weak_reference(_pthread_cond_init, pthread_cond_init); 6075369Sdeischen__weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 6175369Sdeischen__weak_reference(_pthread_cond_signal, pthread_cond_signal); 6275369Sdeischen__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 6371581Sdeischen 6471581Sdeischen 6548046Sjbint 6671581Sdeischen_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 6713546Sjulian{ 6813546Sjulian enum pthread_cond_type type; 6917706Sjulian pthread_cond_t pcond; 70113658Sdeischen int flags; 7113546Sjulian int rval = 0; 7213546Sjulian 7335509Sjb if (cond == NULL) 7431402Salex rval = EINVAL; 7535509Sjb else { 7617706Sjulian /* 7724827Sjb * Check if a pointer to a condition variable attribute 7824827Sjb * structure was passed by the caller: 7917706Sjulian */ 8017706Sjulian if (cond_attr != NULL && *cond_attr != NULL) { 8117706Sjulian /* Default to a fast condition variable: */ 8217706Sjulian type = (*cond_attr)->c_type; 83113658Sdeischen flags = (*cond_attr)->c_flags; 8417706Sjulian } else { 8517706Sjulian /* Default to a fast condition variable: */ 8617706Sjulian type = COND_TYPE_FAST; 87113658Sdeischen flags = 0; 8817706Sjulian } 8913546Sjulian 9017706Sjulian /* Process according to condition variable type: */ 9117706Sjulian switch (type) { 9224827Sjb /* Fast condition variable: */ 9317706Sjulian case COND_TYPE_FAST: 9417706Sjulian /* Nothing to do here. */ 9517706Sjulian break; 9613546Sjulian 9724827Sjb /* Trap invalid condition variable types: */ 9817706Sjulian default: 9917706Sjulian /* Return an invalid argument error: */ 10031402Salex rval = EINVAL; 10117706Sjulian break; 10217706Sjulian } 10313546Sjulian 10417706Sjulian /* Check for no errors: */ 10517706Sjulian if (rval == 0) { 10624827Sjb if ((pcond = (pthread_cond_t) 10724827Sjb malloc(sizeof(struct pthread_cond))) == NULL) { 10831402Salex rval = ENOMEM; 109113658Sdeischen } else if (_lock_init(&pcond->c_lock, LCK_ADAPTIVE, 110113786Sdeischen _thr_lock_wait, _thr_lock_wakeup) != 0) { 111113658Sdeischen free(pcond); 112113658Sdeischen rval = ENOMEM; 11317706Sjulian } else { 11417706Sjulian /* 11517706Sjulian * Initialise the condition variable 11617706Sjulian * structure: 11717706Sjulian */ 11844963Sjb TAILQ_INIT(&pcond->c_queue); 119120069Sdavidxu pcond->c_flags = COND_FLAGS_INITED; 12017706Sjulian pcond->c_type = type; 12144963Sjb pcond->c_mutex = NULL; 12268516Sdeischen pcond->c_seqno = 0; 12317706Sjulian *cond = pcond; 12417706Sjulian } 12517706Sjulian } 12613546Sjulian } 12713546Sjulian /* Return the completion status: */ 12813546Sjulian return (rval); 12913546Sjulian} 13013546Sjulian 13113546Sjulianint 13271581Sdeischen_pthread_cond_destroy(pthread_cond_t *cond) 13313546Sjulian{ 134113658Sdeischen struct pthread_cond *cv; 135113658Sdeischen struct pthread *curthread = _get_curthread(); 136113658Sdeischen int rval = 0; 13713546Sjulian 13835509Sjb if (cond == NULL || *cond == NULL) 13931402Salex rval = EINVAL; 14035509Sjb else { 14135509Sjb /* Lock the condition variable structure: */ 142113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 14313546Sjulian 14435509Sjb /* 145113658Sdeischen * NULL the caller's pointer now that the condition 146113658Sdeischen * variable has been destroyed: 147113658Sdeischen */ 148113658Sdeischen cv = *cond; 149113658Sdeischen *cond = NULL; 150113658Sdeischen 151113658Sdeischen /* Unlock the condition variable structure: */ 152113658Sdeischen THR_LOCK_RELEASE(curthread, &cv->c_lock); 153113658Sdeischen 154115761Sdavidxu /* Free the cond lock structure: */ 155115761Sdavidxu _lock_destroy(&cv->c_lock); 156115761Sdavidxu 157113658Sdeischen /* 15835509Sjb * Free the memory allocated for the condition 15935509Sjb * variable structure: 16035509Sjb */ 161113658Sdeischen free(cv); 16217706Sjulian 16313546Sjulian } 16413546Sjulian /* Return the completion status: */ 16513546Sjulian return (rval); 16613546Sjulian} 16713546Sjulian 16813546Sjulianint 16971581Sdeischen_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 17013546Sjulian{ 17171581Sdeischen struct pthread *curthread = _get_curthread(); 17256277Sjasone int rval = 0; 17368516Sdeischen int done = 0; 17456277Sjasone int interrupted = 0; 175113658Sdeischen int unlock_mutex = 1; 17668516Sdeischen int seqno; 17713546Sjulian 178115173Sdeischen if (cond == NULL) 17968516Sdeischen return (EINVAL); 18035027Sjb 18135027Sjb /* 18235027Sjb * If the condition variable is statically initialized, 18335027Sjb * perform the dynamic initialization: 18435027Sjb */ 18568516Sdeischen if (*cond == NULL && 186115173Sdeischen (rval = pthread_cond_init(cond, NULL)) != 0) 18768516Sdeischen return (rval); 18868516Sdeischen 189115278Sdeischen if (!_kse_isthreaded()) 190115278Sdeischen _kse_setthreaded(1); 191115278Sdeischen 19268516Sdeischen /* 19368516Sdeischen * Enter a loop waiting for a condition signal or broadcast 19468516Sdeischen * to wake up this thread. A loop is needed in case the waiting 19568516Sdeischen * thread is interrupted by a signal to execute a signal handler. 19668516Sdeischen * It is not (currently) possible to remain in the waiting queue 19768516Sdeischen * while running a handler. Instead, the thread is interrupted 19868516Sdeischen * and backed out of the waiting queue prior to executing the 19968516Sdeischen * signal handler. 20068516Sdeischen */ 20168516Sdeischen do { 20248046Sjb /* Lock the condition variable structure: */ 203113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 20448046Sjb 20547424Sjb /* 20647424Sjb * If the condvar was statically allocated, properly 20747424Sjb * initialize the tail queue. 20847424Sjb */ 20947424Sjb if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 21047424Sjb TAILQ_INIT(&(*cond)->c_queue); 21147424Sjb (*cond)->c_flags |= COND_FLAGS_INITED; 21247424Sjb } 21347424Sjb 21417706Sjulian /* Process according to condition variable type: */ 21517706Sjulian switch ((*cond)->c_type) { 21624827Sjb /* Fast condition variable: */ 21717706Sjulian case COND_TYPE_FAST: 21844963Sjb if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 21944963Sjb ((*cond)->c_mutex != *mutex))) { 22044963Sjb /* Unlock the condition variable structure: */ 221113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 22240974Sdt 22344963Sjb /* Return invalid argument error: */ 22444963Sjb rval = EINVAL; 22544963Sjb } else { 22653812Salfred /* Reset the timeout and interrupted flags: */ 22771581Sdeischen curthread->timeout = 0; 22871581Sdeischen curthread->interrupted = 0; 22913546Sjulian 23040974Sdt /* 23144963Sjb * Queue the running thread for the condition 23244963Sjb * variable: 23340974Sdt */ 23471581Sdeischen cond_queue_enq(*cond, curthread); 23513546Sjulian 23668516Sdeischen /* Remember the mutex and sequence number: */ 23744963Sjb (*cond)->c_mutex = *mutex; 23868516Sdeischen seqno = (*cond)->c_seqno; 23935509Sjb 24044963Sjb /* Wait forever: */ 24171581Sdeischen curthread->wakeup_time.tv_sec = -1; 24244963Sjb 24344963Sjb /* Unlock the mutex: */ 244113658Sdeischen if ((unlock_mutex != 0) && 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); 25244963Sjb 25344963Sjb /* Check for no more waiters: */ 254113658Sdeischen if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 25544963Sjb (*cond)->c_mutex = NULL; 25644963Sjb 25744963Sjb /* Unlock the condition variable structure: */ 258113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 259113658Sdeischen } 260113658Sdeischen else { 26144963Sjb /* 262113658Sdeischen * Don't unlock the mutex the next 263113658Sdeischen * time through the loop (if the 264113658Sdeischen * thread has to be requeued after 265113658Sdeischen * handling a signal). 26644963Sjb */ 267113658Sdeischen unlock_mutex = 0; 26844963Sjb 269113658Sdeischen /* 270113658Sdeischen * This thread is active and is in a 271113658Sdeischen * critical region (holding the cv 272113658Sdeischen * lock); we should be able to safely 273113658Sdeischen * set the state. 274113658Sdeischen */ 275115080Sdeischen THR_SCHED_LOCK(curthread, curthread); 276113658Sdeischen THR_SET_STATE(curthread, PS_COND_WAIT); 27756277Sjasone 278113658Sdeischen /* Remember the CV: */ 279113658Sdeischen curthread->data.cond = *cond; 280115080Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 28181918Sjasone 282113658Sdeischen /* Unlock the CV structure: */ 283113658Sdeischen THR_LOCK_RELEASE(curthread, 284113658Sdeischen &(*cond)->c_lock); 285113658Sdeischen 286113658Sdeischen /* Schedule the next thread: */ 287113658Sdeischen _thr_sched_switch(curthread); 288113658Sdeischen 289113658Sdeischen curthread->data.cond = NULL; 290113658Sdeischen 29181918Sjasone /* 292113658Sdeischen * XXX - This really isn't a good check 293113658Sdeischen * since there can be more than one 294113658Sdeischen * thread waiting on the CV. Signals 295113658Sdeischen * sent to threads waiting on mutexes 296113658Sdeischen * or CVs should really be deferred 297113658Sdeischen * until the threads are no longer 298113658Sdeischen * waiting, but POSIX says that signals 299113658Sdeischen * should be sent "as soon as possible". 30081918Sjasone */ 301113658Sdeischen done = (seqno != (*cond)->c_seqno); 302113658Sdeischen 303113658Sdeischen if (THR_IN_SYNCQ(curthread)) { 30456277Sjasone /* 30553812Salfred * Lock the condition variable 30653812Salfred * while removing the thread. 30753812Salfred */ 308113658Sdeischen THR_LOCK_ACQUIRE(curthread, 309113658Sdeischen &(*cond)->c_lock); 31053812Salfred 31153812Salfred cond_queue_remove(*cond, 31271581Sdeischen curthread); 31353812Salfred 31453812Salfred /* Check for no more waiters: */ 31553812Salfred if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 31653812Salfred (*cond)->c_mutex = NULL; 31753812Salfred 318113658Sdeischen THR_LOCK_RELEASE(curthread, 319113658Sdeischen &(*cond)->c_lock); 320113658Sdeischen } 32181918Sjasone 322113658Sdeischen /* 323113658Sdeischen * Save the interrupted flag; locking 324113658Sdeischen * the mutex may destroy it. 325113658Sdeischen */ 326113658Sdeischen interrupted = curthread->interrupted; 327113658Sdeischen 328113658Sdeischen /* 329113658Sdeischen * Note that even though this thread may 330113658Sdeischen * have been canceled, POSIX requires 331113658Sdeischen * that the mutex be reaquired prior to 332113658Sdeischen * cancellation. 333113658Sdeischen */ 334114524Sdavidxu if (done || interrupted) { 33581918Sjasone rval = _mutex_cv_lock(mutex); 336114187Sdeischen unlock_mutex = 1; 337114187Sdeischen } 33844963Sjb } 33940974Sdt } 34017706Sjulian break; 34113546Sjulian 34224827Sjb /* Trap invalid condition variable types: */ 34317706Sjulian default: 34440974Sdt /* Unlock the condition variable structure: */ 345113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 34640974Sdt 34717706Sjulian /* Return an invalid argument error: */ 34831402Salex rval = EINVAL; 34917706Sjulian break; 35017706Sjulian } 35153812Salfred 35271581Sdeischen if ((interrupted != 0) && (curthread->continuation != NULL)) 35371581Sdeischen curthread->continuation((void *) curthread); 35468516Sdeischen } while ((done == 0) && (rval == 0)); 35513546Sjulian 35613546Sjulian /* Return the completion status: */ 35713546Sjulian return (rval); 35813546Sjulian} 35913546Sjulian 360115399Skan__strong_reference(_pthread_cond_wait, _thr_cond_wait); 361115399Skan 36213546Sjulianint 363113658Sdeischen__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 364113658Sdeischen{ 365113658Sdeischen struct pthread *curthread = _get_curthread(); 366113658Sdeischen int ret; 367113658Sdeischen 368113658Sdeischen _thr_enter_cancellation_point(curthread); 369113658Sdeischen ret = _pthread_cond_wait(cond, mutex); 370113658Sdeischen _thr_leave_cancellation_point(curthread); 371113658Sdeischen return (ret); 372113658Sdeischen} 373113658Sdeischen 374113658Sdeischenint 37571581Sdeischen_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 37613546Sjulian const struct timespec * abstime) 37713546Sjulian{ 37871581Sdeischen struct pthread *curthread = _get_curthread(); 37956277Sjasone int rval = 0; 38068516Sdeischen int done = 0; 38156277Sjasone int interrupted = 0; 382113658Sdeischen int unlock_mutex = 1; 38368516Sdeischen int seqno; 38413546Sjulian 385113870Sdeischen THR_ASSERT(curthread->locklevel == 0, 386113870Sdeischen "cv_timedwait: locklevel is not zero!"); 387113658Sdeischen 38863355Sjasone if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 389115173Sdeischen abstime->tv_nsec >= 1000000000) 39068516Sdeischen return (EINVAL); 39135027Sjb /* 39263355Sjasone * If the condition variable is statically initialized, perform dynamic 39363355Sjasone * initialization. 39435027Sjb */ 395115173Sdeischen if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0) 39668516Sdeischen return (rval); 39768516Sdeischen 398115278Sdeischen if (!_kse_isthreaded()) 399115278Sdeischen _kse_setthreaded(1); 400115278Sdeischen 40168516Sdeischen /* 40268516Sdeischen * Enter a loop waiting for a condition signal or broadcast 40368516Sdeischen * to wake up this thread. A loop is needed in case the waiting 40468516Sdeischen * thread is interrupted by a signal to execute a signal handler. 40568516Sdeischen * It is not (currently) possible to remain in the waiting queue 40668516Sdeischen * while running a handler. Instead, the thread is interrupted 40768516Sdeischen * and backed out of the waiting queue prior to executing the 40868516Sdeischen * signal handler. 40968516Sdeischen */ 41068516Sdeischen do { 41148046Sjb /* Lock the condition variable structure: */ 412113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 41348046Sjb 41447424Sjb /* 41547424Sjb * If the condvar was statically allocated, properly 41647424Sjb * initialize the tail queue. 41747424Sjb */ 41847424Sjb if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 41947424Sjb TAILQ_INIT(&(*cond)->c_queue); 42047424Sjb (*cond)->c_flags |= COND_FLAGS_INITED; 42147424Sjb } 42247424Sjb 42317706Sjulian /* Process according to condition variable type: */ 42417706Sjulian switch ((*cond)->c_type) { 42524827Sjb /* Fast condition variable: */ 42617706Sjulian case COND_TYPE_FAST: 42744963Sjb if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 42844963Sjb ((*cond)->c_mutex != *mutex))) { 42944963Sjb /* Return invalid argument error: */ 43044963Sjb rval = EINVAL; 43113546Sjulian 43244963Sjb /* Unlock the condition variable structure: */ 433113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 43444963Sjb } else { 43544963Sjb /* Set the wakeup time: */ 436113658Sdeischen curthread->wakeup_time.tv_sec = abstime->tv_sec; 43771581Sdeischen curthread->wakeup_time.tv_nsec = 43844963Sjb abstime->tv_nsec; 43913546Sjulian 44053812Salfred /* Reset the timeout and interrupted flags: */ 44171581Sdeischen curthread->timeout = 0; 44271581Sdeischen curthread->interrupted = 0; 44313546Sjulian 44417706Sjulian /* 44544963Sjb * Queue the running thread for the condition 44644963Sjb * variable: 44717706Sjulian */ 44871581Sdeischen cond_queue_enq(*cond, curthread); 44940974Sdt 45068516Sdeischen /* Remember the mutex and sequence number: */ 45144963Sjb (*cond)->c_mutex = *mutex; 45268516Sdeischen seqno = (*cond)->c_seqno; 45313546Sjulian 45444963Sjb /* Unlock the mutex: */ 455113658Sdeischen if ((unlock_mutex != 0) && 456113658Sdeischen ((rval = _mutex_cv_unlock(mutex)) != 0)) { 45744963Sjb /* 458113658Sdeischen * Cannot unlock the mutex; remove the 459113658Sdeischen * running thread from the condition 46044963Sjb * variable queue: 46144963Sjb */ 46271581Sdeischen cond_queue_remove(*cond, curthread); 46344963Sjb 46444963Sjb /* Check for no more waiters: */ 46544963Sjb if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 46644963Sjb (*cond)->c_mutex = NULL; 46744963Sjb 46844963Sjb /* Unlock the condition variable structure: */ 469113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 47044963Sjb } else { 47144963Sjb /* 472113658Sdeischen * Don't unlock the mutex the next 473113658Sdeischen * time through the loop (if the 474113658Sdeischen * thread has to be requeued after 475113658Sdeischen * handling a signal). 47644963Sjb */ 477113658Sdeischen unlock_mutex = 0; 47844963Sjb 479113658Sdeischen /* 480113658Sdeischen * This thread is active and is in a 481113658Sdeischen * critical region (holding the cv 482113658Sdeischen * lock); we should be able to safely 483113658Sdeischen * set the state. 484113658Sdeischen */ 485115080Sdeischen THR_SCHED_LOCK(curthread, curthread); 486113658Sdeischen THR_SET_STATE(curthread, PS_COND_WAIT); 48768516Sdeischen 488113658Sdeischen /* Remember the CV: */ 489113658Sdeischen curthread->data.cond = *cond; 490115080Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 49181918Sjasone 492113658Sdeischen /* Unlock the CV structure: */ 493113658Sdeischen THR_LOCK_RELEASE(curthread, 494113658Sdeischen &(*cond)->c_lock); 495113658Sdeischen 496113658Sdeischen /* Schedule the next thread: */ 497113658Sdeischen _thr_sched_switch(curthread); 498113658Sdeischen 499113658Sdeischen curthread->data.cond = NULL; 500113658Sdeischen 50153812Salfred /* 502113658Sdeischen * XXX - This really isn't a good check 503113658Sdeischen * since there can be more than one 504113658Sdeischen * thread waiting on the CV. Signals 505113658Sdeischen * sent to threads waiting on mutexes 506113658Sdeischen * or CVs should really be deferred 507113658Sdeischen * until the threads are no longer 508113658Sdeischen * waiting, but POSIX says that signals 509113658Sdeischen * should be sent "as soon as possible". 51053812Salfred */ 511113658Sdeischen done = (seqno != (*cond)->c_seqno); 512113658Sdeischen 513113658Sdeischen if (THR_IN_CONDQ(curthread)) { 51481918Sjasone /* 51581918Sjasone * Lock the condition variable 51681918Sjasone * while removing the thread. 51781918Sjasone */ 518113658Sdeischen THR_LOCK_ACQUIRE(curthread, 519113658Sdeischen &(*cond)->c_lock); 52044963Sjb 52144963Sjb cond_queue_remove(*cond, 52271581Sdeischen curthread); 52344963Sjb 52444963Sjb /* Check for no more waiters: */ 52544963Sjb if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 52644963Sjb (*cond)->c_mutex = NULL; 52744963Sjb 528113658Sdeischen THR_LOCK_RELEASE(curthread, 529113658Sdeischen &(*cond)->c_lock); 530113658Sdeischen } 53144963Sjb 532113658Sdeischen /* 533113658Sdeischen * Save the interrupted flag; locking 534113658Sdeischen * the mutex may destroy it. 535113658Sdeischen */ 536113658Sdeischen interrupted = curthread->interrupted; 537113658Sdeischen if (curthread->timeout != 0) { 538113658Sdeischen /* The wait timedout. */ 539113658Sdeischen rval = ETIMEDOUT; 540113658Sdeischen (void)_mutex_cv_lock(mutex); 541117165Sdavidxu } else if (interrupted || done) { 54281918Sjasone rval = _mutex_cv_lock(mutex); 543117165Sdavidxu unlock_mutex = 1; 544117165Sdavidxu } 54517706Sjulian } 54613546Sjulian } 54717706Sjulian break; 54817706Sjulian 54924827Sjb /* Trap invalid condition variable types: */ 55017706Sjulian default: 55140974Sdt /* Unlock the condition variable structure: */ 552113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 55340974Sdt 55417706Sjulian /* Return an invalid argument error: */ 55531402Salex rval = EINVAL; 55617706Sjulian break; 55713546Sjulian } 55813546Sjulian 55971581Sdeischen if ((interrupted != 0) && (curthread->continuation != NULL)) 560113658Sdeischen curthread->continuation((void *)curthread); 56168516Sdeischen } while ((done == 0) && (rval == 0)); 56213546Sjulian 56313546Sjulian /* Return the completion status: */ 56413546Sjulian return (rval); 56513546Sjulian} 56613546Sjulian 56713546Sjulianint 568113658Sdeischen__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 569113658Sdeischen const struct timespec *abstime) 570113658Sdeischen{ 571113658Sdeischen struct pthread *curthread = _get_curthread(); 572113658Sdeischen int ret; 573113658Sdeischen 574113658Sdeischen _thr_enter_cancellation_point(curthread); 575113658Sdeischen ret = _pthread_cond_timedwait(cond, mutex, abstime); 576113658Sdeischen _thr_leave_cancellation_point(curthread); 577113658Sdeischen return (ret); 578113658Sdeischen} 579113658Sdeischen 580113658Sdeischen 581113658Sdeischenint 58271581Sdeischen_pthread_cond_signal(pthread_cond_t * cond) 58313546Sjulian{ 584113658Sdeischen struct pthread *curthread = _get_curthread(); 585113658Sdeischen struct pthread *pthread; 586117907Sdeischen struct kse_mailbox *kmbx; 587113658Sdeischen int rval = 0; 58813546Sjulian 589113870Sdeischen THR_ASSERT(curthread->locklevel == 0, 590113870Sdeischen "cv_timedwait: locklevel is not zero!"); 59163355Sjasone if (cond == NULL) 59231402Salex rval = EINVAL; 59363355Sjasone /* 59463355Sjasone * If the condition variable is statically initialized, perform dynamic 59563355Sjasone * initialization. 59663355Sjasone */ 59768844Sdeischen else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) { 59835509Sjb /* Lock the condition variable structure: */ 599113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 60013546Sjulian 60117706Sjulian /* Process according to condition variable type: */ 60217706Sjulian switch ((*cond)->c_type) { 60324827Sjb /* Fast condition variable: */ 60417706Sjulian case COND_TYPE_FAST: 60568516Sdeischen /* Increment the sequence number: */ 60668516Sdeischen (*cond)->c_seqno++; 60768516Sdeischen 608113658Sdeischen /* 609113658Sdeischen * Wakeups have to be done with the CV lock held; 610113658Sdeischen * otherwise there is a race condition where the 611113658Sdeischen * thread can timeout, run on another KSE, and enter 612113658Sdeischen * another blocking state (including blocking on a CV). 613113658Sdeischen */ 614113658Sdeischen if ((pthread = TAILQ_FIRST(&(*cond)->c_queue)) 615113658Sdeischen != NULL) { 616113658Sdeischen THR_SCHED_LOCK(curthread, pthread); 617113658Sdeischen cond_queue_remove(*cond, pthread); 618117714Sdeischen if ((pthread->kseg == curthread->kseg) && 619117714Sdeischen (pthread->active_priority > 620117714Sdeischen curthread->active_priority)) 621117714Sdeischen curthread->critical_yield = 1; 622117907Sdeischen kmbx = _thr_setrunnable_unlocked(pthread); 623113658Sdeischen THR_SCHED_UNLOCK(curthread, pthread); 624117907Sdeischen if (kmbx != NULL) 625117907Sdeischen kse_wakeup(kmbx); 62661681Sjasone } 62744963Sjb /* Check for no more waiters: */ 62844963Sjb if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 62944963Sjb (*cond)->c_mutex = NULL; 63017706Sjulian break; 63117706Sjulian 63224827Sjb /* Trap invalid condition variable types: */ 63317706Sjulian default: 63417706Sjulian /* Return an invalid argument error: */ 63531402Salex rval = EINVAL; 63617706Sjulian break; 63713546Sjulian } 63813546Sjulian 63935509Sjb /* Unlock the condition variable structure: */ 640113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 64113546Sjulian } 64213546Sjulian 64313546Sjulian /* Return the completion status: */ 64413546Sjulian return (rval); 64513546Sjulian} 64613546Sjulian 647115399Skan__strong_reference(_pthread_cond_signal, _thr_cond_signal); 648115399Skan 64913546Sjulianint 65071581Sdeischen_pthread_cond_broadcast(pthread_cond_t * cond) 65113546Sjulian{ 652113658Sdeischen struct pthread *curthread = _get_curthread(); 653113658Sdeischen struct pthread *pthread; 654117907Sdeischen struct kse_mailbox *kmbx; 655113658Sdeischen int rval = 0; 65613546Sjulian 657113870Sdeischen THR_ASSERT(curthread->locklevel == 0, 658113870Sdeischen "cv_timedwait: locklevel is not zero!"); 65963355Sjasone if (cond == NULL) 66035509Sjb rval = EINVAL; 66163355Sjasone /* 66263355Sjasone * If the condition variable is statically initialized, perform dynamic 66363355Sjasone * initialization. 66463355Sjasone */ 66568844Sdeischen else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) { 66635509Sjb /* Lock the condition variable structure: */ 667113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 66813546Sjulian 66935509Sjb /* Process according to condition variable type: */ 67035509Sjb switch ((*cond)->c_type) { 67135509Sjb /* Fast condition variable: */ 67235509Sjb case COND_TYPE_FAST: 67368516Sdeischen /* Increment the sequence number: */ 67468516Sdeischen (*cond)->c_seqno++; 67568516Sdeischen 67635509Sjb /* 67735509Sjb * Enter a loop to bring all threads off the 67835509Sjb * condition queue: 67935509Sjb */ 680113658Sdeischen while ((pthread = TAILQ_FIRST(&(*cond)->c_queue)) 681113658Sdeischen != NULL) { 682113658Sdeischen THR_SCHED_LOCK(curthread, pthread); 683113658Sdeischen cond_queue_remove(*cond, pthread); 684117714Sdeischen if ((pthread->kseg == curthread->kseg) && 685117714Sdeischen (pthread->active_priority > 686117714Sdeischen curthread->active_priority)) 687117714Sdeischen curthread->critical_yield = 1; 688117907Sdeischen kmbx = _thr_setrunnable_unlocked(pthread); 689113658Sdeischen THR_SCHED_UNLOCK(curthread, pthread); 690117907Sdeischen if (kmbx != NULL) 691117907Sdeischen kse_wakeup(kmbx); 69235509Sjb } 69344963Sjb 69444963Sjb /* There are no more waiting threads: */ 69544963Sjb (*cond)->c_mutex = NULL; 69635509Sjb break; 697115399Skan 69835509Sjb /* Trap invalid condition variable types: */ 69935509Sjb default: 70035509Sjb /* Return an invalid argument error: */ 70135509Sjb rval = EINVAL; 70235509Sjb break; 70313546Sjulian } 70413546Sjulian 70535509Sjb /* Unlock the condition variable structure: */ 706113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 70713546Sjulian } 70813546Sjulian 70913546Sjulian /* Return the completion status: */ 71013546Sjulian return (rval); 71113546Sjulian} 71244963Sjb 713115399Skan__strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast); 714115399Skan 71567097Sdeischenvoid 716113658Sdeischen_cond_wait_backout(struct pthread *curthread) 71767097Sdeischen{ 71867097Sdeischen pthread_cond_t cond; 71967097Sdeischen 720113658Sdeischen cond = curthread->data.cond; 72167097Sdeischen if (cond != NULL) { 72267097Sdeischen /* Lock the condition variable structure: */ 723113658Sdeischen THR_LOCK_ACQUIRE(curthread, &cond->c_lock); 72467097Sdeischen 72567097Sdeischen /* Process according to condition variable type: */ 72667097Sdeischen switch (cond->c_type) { 72767097Sdeischen /* Fast condition variable: */ 72867097Sdeischen case COND_TYPE_FAST: 729113658Sdeischen cond_queue_remove(cond, curthread); 73067097Sdeischen 73167097Sdeischen /* Check for no more waiters: */ 73267097Sdeischen if (TAILQ_FIRST(&cond->c_queue) == NULL) 73367097Sdeischen cond->c_mutex = NULL; 73467097Sdeischen break; 73567097Sdeischen 73667097Sdeischen default: 73767097Sdeischen break; 73867097Sdeischen } 73967097Sdeischen 74067097Sdeischen /* Unlock the condition variable structure: */ 741113658Sdeischen THR_LOCK_RELEASE(curthread, &cond->c_lock); 74267097Sdeischen } 74367097Sdeischen} 74467097Sdeischen 74544963Sjb/* 74644963Sjb * Dequeue a waiting thread from the head of a condition queue in 74744963Sjb * descending priority order. 74844963Sjb */ 749113658Sdeischenstatic inline struct pthread * 75044963Sjbcond_queue_deq(pthread_cond_t cond) 75144963Sjb{ 752113658Sdeischen struct pthread *pthread; 75344963Sjb 75453812Salfred while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) { 75567097Sdeischen TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 756117165Sdavidxu THR_CONDQ_CLEAR(pthread); 75753812Salfred if ((pthread->timeout == 0) && (pthread->interrupted == 0)) 75853812Salfred /* 75953812Salfred * Only exit the loop when we find a thread 76053812Salfred * that hasn't timed out or been canceled; 76153812Salfred * those threads are already running and don't 76253812Salfred * need their run state changed. 76353812Salfred */ 76453812Salfred break; 76544963Sjb } 76644963Sjb 767113658Sdeischen return (pthread); 76844963Sjb} 76944963Sjb 77044963Sjb/* 77144963Sjb * Remove a waiting thread from a condition queue in descending priority 77244963Sjb * order. 77344963Sjb */ 77444963Sjbstatic inline void 775113658Sdeischencond_queue_remove(pthread_cond_t cond, struct pthread *pthread) 77644963Sjb{ 77744963Sjb /* 77844963Sjb * Because pthread_cond_timedwait() can timeout as well 77944963Sjb * as be signaled by another thread, it is necessary to 78044963Sjb * guard against removing the thread from the queue if 78144963Sjb * it isn't in the queue. 78244963Sjb */ 783113658Sdeischen if (THR_IN_CONDQ(pthread)) { 78467097Sdeischen TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 785113658Sdeischen THR_CONDQ_CLEAR(pthread); 78644963Sjb } 78744963Sjb} 78844963Sjb 78944963Sjb/* 79044963Sjb * Enqueue a waiting thread to a condition queue in descending priority 79144963Sjb * order. 79244963Sjb */ 79344963Sjbstatic inline void 794113658Sdeischencond_queue_enq(pthread_cond_t cond, struct pthread *pthread) 79544963Sjb{ 796113658Sdeischen struct pthread *tid = TAILQ_LAST(&cond->c_queue, cond_head); 79744963Sjb 798113658Sdeischen THR_ASSERT(!THR_IN_SYNCQ(pthread), 799113658Sdeischen "cond_queue_enq: thread already queued!"); 80067097Sdeischen 80144963Sjb /* 80244963Sjb * For the common case of all threads having equal priority, 80344963Sjb * we perform a quick check against the priority of the thread 80444963Sjb * at the tail of the queue. 80544963Sjb */ 80644963Sjb if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) 80767097Sdeischen TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe); 80844963Sjb else { 80944963Sjb tid = TAILQ_FIRST(&cond->c_queue); 81044963Sjb while (pthread->active_priority <= tid->active_priority) 81167097Sdeischen tid = TAILQ_NEXT(tid, sqe); 81267097Sdeischen TAILQ_INSERT_BEFORE(tid, pthread, sqe); 81344963Sjb } 814113658Sdeischen THR_CONDQ_SET(pthread); 81567097Sdeischen pthread->data.cond = cond; 81644963Sjb} 817