thr_cond.c revision 139023
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 139023 2004-12-18 18:07:37Z 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_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 54115173Sdeischen/* 55115173Sdeischen * Double underscore versions are cancellation points. Single underscore 56115173Sdeischen * versions are not and are provided for libc internal usage (which 57115173Sdeischen * shouldn't introduce cancellation points). 58115173Sdeischen */ 59115173Sdeischen__weak_reference(__pthread_cond_wait, pthread_cond_wait); 60115173Sdeischen__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 61115173Sdeischen 6275369Sdeischen__weak_reference(_pthread_cond_init, pthread_cond_init); 6375369Sdeischen__weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 6475369Sdeischen__weak_reference(_pthread_cond_signal, pthread_cond_signal); 6575369Sdeischen__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 6671581Sdeischen 6771581Sdeischen 6848046Sjbint 6971581Sdeischen_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 7013546Sjulian{ 7113546Sjulian enum pthread_cond_type type; 7217706Sjulian pthread_cond_t pcond; 73113658Sdeischen int flags; 7413546Sjulian int rval = 0; 7513546Sjulian 7635509Sjb if (cond == NULL) 7731402Salex rval = EINVAL; 7835509Sjb else { 7917706Sjulian /* 8024827Sjb * Check if a pointer to a condition variable attribute 8124827Sjb * structure was passed by the caller: 8217706Sjulian */ 8317706Sjulian if (cond_attr != NULL && *cond_attr != NULL) { 8417706Sjulian /* Default to a fast condition variable: */ 8517706Sjulian type = (*cond_attr)->c_type; 86113658Sdeischen flags = (*cond_attr)->c_flags; 8717706Sjulian } else { 8817706Sjulian /* Default to a fast condition variable: */ 8917706Sjulian type = COND_TYPE_FAST; 90113658Sdeischen flags = 0; 9117706Sjulian } 9213546Sjulian 9317706Sjulian /* Process according to condition variable type: */ 9417706Sjulian switch (type) { 9524827Sjb /* Fast condition variable: */ 9617706Sjulian case COND_TYPE_FAST: 9717706Sjulian /* Nothing to do here. */ 9817706Sjulian break; 9913546Sjulian 10024827Sjb /* Trap invalid condition variable types: */ 10117706Sjulian default: 10217706Sjulian /* Return an invalid argument error: */ 10331402Salex rval = EINVAL; 10417706Sjulian break; 10517706Sjulian } 10613546Sjulian 10717706Sjulian /* Check for no errors: */ 10817706Sjulian if (rval == 0) { 10924827Sjb if ((pcond = (pthread_cond_t) 11024827Sjb malloc(sizeof(struct pthread_cond))) == NULL) { 11131402Salex rval = ENOMEM; 112113658Sdeischen } else if (_lock_init(&pcond->c_lock, LCK_ADAPTIVE, 113113786Sdeischen _thr_lock_wait, _thr_lock_wakeup) != 0) { 114113658Sdeischen free(pcond); 115113658Sdeischen rval = ENOMEM; 11617706Sjulian } else { 11717706Sjulian /* 11817706Sjulian * Initialise the condition variable 11917706Sjulian * structure: 12017706Sjulian */ 12144963Sjb TAILQ_INIT(&pcond->c_queue); 122120069Sdavidxu pcond->c_flags = COND_FLAGS_INITED; 12317706Sjulian pcond->c_type = type; 12444963Sjb pcond->c_mutex = NULL; 12568516Sdeischen pcond->c_seqno = 0; 12617706Sjulian *cond = pcond; 12717706Sjulian } 12817706Sjulian } 12913546Sjulian } 13013546Sjulian /* Return the completion status: */ 13113546Sjulian return (rval); 13213546Sjulian} 13313546Sjulian 13413546Sjulianint 13571581Sdeischen_pthread_cond_destroy(pthread_cond_t *cond) 13613546Sjulian{ 137113658Sdeischen struct pthread_cond *cv; 138113658Sdeischen struct pthread *curthread = _get_curthread(); 139113658Sdeischen int rval = 0; 14013546Sjulian 14135509Sjb if (cond == NULL || *cond == NULL) 14231402Salex rval = EINVAL; 14335509Sjb else { 14435509Sjb /* Lock the condition variable structure: */ 145113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 14613546Sjulian 14735509Sjb /* 148113658Sdeischen * NULL the caller's pointer now that the condition 149113658Sdeischen * variable has been destroyed: 150113658Sdeischen */ 151113658Sdeischen cv = *cond; 152113658Sdeischen *cond = NULL; 153113658Sdeischen 154113658Sdeischen /* Unlock the condition variable structure: */ 155113658Sdeischen THR_LOCK_RELEASE(curthread, &cv->c_lock); 156113658Sdeischen 157115761Sdavidxu /* Free the cond lock structure: */ 158115761Sdavidxu _lock_destroy(&cv->c_lock); 159115761Sdavidxu 160113658Sdeischen /* 16135509Sjb * Free the memory allocated for the condition 16235509Sjb * variable structure: 16335509Sjb */ 164113658Sdeischen free(cv); 16517706Sjulian 16613546Sjulian } 16713546Sjulian /* Return the completion status: */ 16813546Sjulian return (rval); 16913546Sjulian} 17013546Sjulian 17113546Sjulianint 17271581Sdeischen_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 17313546Sjulian{ 17471581Sdeischen struct pthread *curthread = _get_curthread(); 17556277Sjasone int rval = 0; 17668516Sdeischen int done = 0; 177139023Sdeischen int mutex_locked = 1; 17868516Sdeischen int seqno; 17913546Sjulian 180115173Sdeischen if (cond == NULL) 18168516Sdeischen return (EINVAL); 18235027Sjb 18335027Sjb /* 18435027Sjb * If the condition variable is statically initialized, 18535027Sjb * perform the dynamic initialization: 18635027Sjb */ 18768516Sdeischen if (*cond == NULL && 188115173Sdeischen (rval = pthread_cond_init(cond, NULL)) != 0) 18968516Sdeischen return (rval); 19068516Sdeischen 191115278Sdeischen if (!_kse_isthreaded()) 192115278Sdeischen _kse_setthreaded(1); 193115278Sdeischen 19468516Sdeischen /* 19568516Sdeischen * Enter a loop waiting for a condition signal or broadcast 19668516Sdeischen * to wake up this thread. A loop is needed in case the waiting 19768516Sdeischen * thread is interrupted by a signal to execute a signal handler. 19868516Sdeischen * It is not (currently) possible to remain in the waiting queue 19968516Sdeischen * while running a handler. Instead, the thread is interrupted 20068516Sdeischen * and backed out of the waiting queue prior to executing the 20168516Sdeischen * signal handler. 20268516Sdeischen */ 203139023Sdeischen 204139023Sdeischen /* Lock the condition variable structure: */ 205139023Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 206139023Sdeischen seqno = (*cond)->c_seqno; 20768516Sdeischen do { 20847424Sjb /* 20947424Sjb * If the condvar was statically allocated, properly 21047424Sjb * initialize the tail queue. 21147424Sjb */ 21247424Sjb if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 21347424Sjb TAILQ_INIT(&(*cond)->c_queue); 21447424Sjb (*cond)->c_flags |= COND_FLAGS_INITED; 21547424Sjb } 21647424Sjb 21717706Sjulian /* Process according to condition variable type: */ 21817706Sjulian switch ((*cond)->c_type) { 21924827Sjb /* Fast condition variable: */ 22017706Sjulian case COND_TYPE_FAST: 22144963Sjb if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 22244963Sjb ((*cond)->c_mutex != *mutex))) { 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 23644963Sjb /* Wait forever: */ 23771581Sdeischen curthread->wakeup_time.tv_sec = -1; 23844963Sjb 23944963Sjb /* Unlock the mutex: */ 240139023Sdeischen if (mutex_locked && 241113658Sdeischen ((rval = _mutex_cv_unlock(mutex)) != 0)) { 24244963Sjb /* 24344963Sjb * Cannot unlock the mutex, so remove 24444963Sjb * the running thread from the condition 24544963Sjb * variable queue: 24644963Sjb */ 24771581Sdeischen cond_queue_remove(*cond, curthread); 248113658Sdeischen } 249113658Sdeischen else { 250139023Sdeischen /* Remember the mutex: */ 251139023Sdeischen (*cond)->c_mutex = *mutex; 252139023Sdeischen 25344963Sjb /* 254113658Sdeischen * Don't unlock the mutex the next 255113658Sdeischen * time through the loop (if the 256113658Sdeischen * thread has to be requeued after 257113658Sdeischen * handling a signal). 25844963Sjb */ 259139023Sdeischen mutex_locked = 0; 26044963Sjb 261113658Sdeischen /* 262113658Sdeischen * This thread is active and is in a 263113658Sdeischen * critical region (holding the cv 264113658Sdeischen * lock); we should be able to safely 265113658Sdeischen * set the state. 266113658Sdeischen */ 267115080Sdeischen THR_SCHED_LOCK(curthread, curthread); 268113658Sdeischen THR_SET_STATE(curthread, PS_COND_WAIT); 26956277Sjasone 270113658Sdeischen /* Remember the CV: */ 271113658Sdeischen curthread->data.cond = *cond; 272139023Sdeischen curthread->sigbackout = cond_wait_backout; 273115080Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 27481918Sjasone 275113658Sdeischen /* Unlock the CV structure: */ 276113658Sdeischen THR_LOCK_RELEASE(curthread, 277113658Sdeischen &(*cond)->c_lock); 278113658Sdeischen 279113658Sdeischen /* Schedule the next thread: */ 280113658Sdeischen _thr_sched_switch(curthread); 281113658Sdeischen 28281918Sjasone /* 283113658Sdeischen * XXX - This really isn't a good check 284113658Sdeischen * since there can be more than one 285113658Sdeischen * thread waiting on the CV. Signals 286113658Sdeischen * sent to threads waiting on mutexes 287113658Sdeischen * or CVs should really be deferred 288113658Sdeischen * until the threads are no longer 289113658Sdeischen * waiting, but POSIX says that signals 290113658Sdeischen * should be sent "as soon as possible". 29181918Sjasone */ 292113658Sdeischen done = (seqno != (*cond)->c_seqno); 293139023Sdeischen if (done && !THR_IN_CONDQ(curthread)) { 29456277Sjasone /* 295139023Sdeischen * The thread is dequeued, so 296139023Sdeischen * it is safe to clear these. 29753812Salfred */ 298139023Sdeischen curthread->data.cond = NULL; 299139023Sdeischen curthread->sigbackout = NULL; 300139023Sdeischen check_continuation(curthread, 301139023Sdeischen NULL, mutex); 302139023Sdeischen return (_mutex_cv_lock(mutex)); 303139023Sdeischen } 30453812Salfred 305139023Sdeischen /* Relock the CV structure: */ 306139023Sdeischen THR_LOCK_ACQUIRE(curthread, 307139023Sdeischen &(*cond)->c_lock); 308139023Sdeischen 309139023Sdeischen /* 310139023Sdeischen * Clear these after taking the lock to 311139023Sdeischen * prevent a race condition where a 312139023Sdeischen * signal can arrive before dequeueing 313139023Sdeischen * the thread. 314139023Sdeischen */ 315139023Sdeischen curthread->data.cond = NULL; 316139023Sdeischen curthread->sigbackout = NULL; 317139023Sdeischen done = (seqno != (*cond)->c_seqno); 318139023Sdeischen 319139023Sdeischen if (THR_IN_CONDQ(curthread)) { 32053812Salfred cond_queue_remove(*cond, 32171581Sdeischen curthread); 32253812Salfred 32353812Salfred /* Check for no more waiters: */ 32453812Salfred if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 32553812Salfred (*cond)->c_mutex = NULL; 326113658Sdeischen } 32744963Sjb } 32840974Sdt } 32917706Sjulian break; 33013546Sjulian 33124827Sjb /* Trap invalid condition variable types: */ 33217706Sjulian default: 33317706Sjulian /* Return an invalid argument error: */ 33431402Salex rval = EINVAL; 33517706Sjulian break; 33617706Sjulian } 33753812Salfred 338139023Sdeischen check_continuation(curthread, *cond, 339139023Sdeischen mutex_locked ? NULL : mutex); 34068516Sdeischen } while ((done == 0) && (rval == 0)); 34113546Sjulian 342139023Sdeischen /* Unlock the condition variable structure: */ 343139023Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 344139023Sdeischen 345139023Sdeischen if (mutex_locked == 0) 346139023Sdeischen _mutex_cv_lock(mutex); 347139023Sdeischen 34813546Sjulian /* Return the completion status: */ 34913546Sjulian return (rval); 35013546Sjulian} 35113546Sjulian 352115399Skan__strong_reference(_pthread_cond_wait, _thr_cond_wait); 353115399Skan 35413546Sjulianint 355113658Sdeischen__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 356113658Sdeischen{ 357113658Sdeischen struct pthread *curthread = _get_curthread(); 358113658Sdeischen int ret; 359113658Sdeischen 360123312Sdavidxu _thr_cancel_enter(curthread); 361113658Sdeischen ret = _pthread_cond_wait(cond, mutex); 362123312Sdavidxu _thr_cancel_leave(curthread, 1); 363113658Sdeischen return (ret); 364113658Sdeischen} 365113658Sdeischen 366113658Sdeischenint 36771581Sdeischen_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 36813546Sjulian const struct timespec * abstime) 36913546Sjulian{ 37071581Sdeischen struct pthread *curthread = _get_curthread(); 37156277Sjasone int rval = 0; 37268516Sdeischen int done = 0; 373139023Sdeischen int mutex_locked = 1; 37468516Sdeischen int seqno; 37513546Sjulian 376113870Sdeischen THR_ASSERT(curthread->locklevel == 0, 377113870Sdeischen "cv_timedwait: locklevel is not zero!"); 378113658Sdeischen 37963355Sjasone if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 380115173Sdeischen abstime->tv_nsec >= 1000000000) 38168516Sdeischen return (EINVAL); 38235027Sjb /* 38363355Sjasone * If the condition variable is statically initialized, perform dynamic 38463355Sjasone * initialization. 38535027Sjb */ 386115173Sdeischen if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0) 38768516Sdeischen return (rval); 38868516Sdeischen 389115278Sdeischen if (!_kse_isthreaded()) 390115278Sdeischen _kse_setthreaded(1); 391115278Sdeischen 39268516Sdeischen /* 39368516Sdeischen * Enter a loop waiting for a condition signal or broadcast 39468516Sdeischen * to wake up this thread. A loop is needed in case the waiting 39568516Sdeischen * thread is interrupted by a signal to execute a signal handler. 39668516Sdeischen * It is not (currently) possible to remain in the waiting queue 39768516Sdeischen * while running a handler. Instead, the thread is interrupted 39868516Sdeischen * and backed out of the waiting queue prior to executing the 39968516Sdeischen * signal handler. 40068516Sdeischen */ 401139023Sdeischen 402139023Sdeischen /* Lock the condition variable structure: */ 403139023Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 404139023Sdeischen seqno = (*cond)->c_seqno; 40568516Sdeischen do { 40647424Sjb /* 40747424Sjb * If the condvar was statically allocated, properly 40847424Sjb * initialize the tail queue. 40947424Sjb */ 41047424Sjb if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 41147424Sjb TAILQ_INIT(&(*cond)->c_queue); 41247424Sjb (*cond)->c_flags |= COND_FLAGS_INITED; 41347424Sjb } 41447424Sjb 41517706Sjulian /* Process according to condition variable type: */ 41617706Sjulian switch ((*cond)->c_type) { 41724827Sjb /* Fast condition variable: */ 41817706Sjulian case COND_TYPE_FAST: 41944963Sjb if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 42044963Sjb ((*cond)->c_mutex != *mutex))) { 42144963Sjb /* Return invalid argument error: */ 42244963Sjb rval = EINVAL; 42344963Sjb } else { 42444963Sjb /* Set the wakeup time: */ 425113658Sdeischen curthread->wakeup_time.tv_sec = abstime->tv_sec; 42671581Sdeischen curthread->wakeup_time.tv_nsec = 42744963Sjb abstime->tv_nsec; 42813546Sjulian 42953812Salfred /* Reset the timeout and interrupted flags: */ 43071581Sdeischen curthread->timeout = 0; 43171581Sdeischen curthread->interrupted = 0; 43213546Sjulian 43317706Sjulian /* 43444963Sjb * Queue the running thread for the condition 43544963Sjb * variable: 43617706Sjulian */ 43771581Sdeischen cond_queue_enq(*cond, curthread); 43840974Sdt 43944963Sjb /* Unlock the mutex: */ 440139023Sdeischen if (mutex_locked && 441113658Sdeischen ((rval = _mutex_cv_unlock(mutex)) != 0)) { 44244963Sjb /* 443113658Sdeischen * Cannot unlock the mutex; remove the 444113658Sdeischen * running thread from the condition 44544963Sjb * variable queue: 44644963Sjb */ 44771581Sdeischen cond_queue_remove(*cond, curthread); 448139023Sdeischen } else { 449139023Sdeischen /* Remember the mutex: */ 450139023Sdeischen (*cond)->c_mutex = *mutex; 45144963Sjb 45244963Sjb /* 453113658Sdeischen * Don't unlock the mutex the next 454113658Sdeischen * time through the loop (if the 455113658Sdeischen * thread has to be requeued after 456113658Sdeischen * handling a signal). 45744963Sjb */ 458139023Sdeischen mutex_locked = 0; 45944963Sjb 460113658Sdeischen /* 461113658Sdeischen * This thread is active and is in a 462113658Sdeischen * critical region (holding the cv 463113658Sdeischen * lock); we should be able to safely 464113658Sdeischen * set the state. 465113658Sdeischen */ 466115080Sdeischen THR_SCHED_LOCK(curthread, curthread); 467113658Sdeischen THR_SET_STATE(curthread, PS_COND_WAIT); 46868516Sdeischen 469113658Sdeischen /* Remember the CV: */ 470113658Sdeischen curthread->data.cond = *cond; 471139023Sdeischen curthread->sigbackout = cond_wait_backout; 472115080Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 47381918Sjasone 474113658Sdeischen /* Unlock the CV structure: */ 475113658Sdeischen THR_LOCK_RELEASE(curthread, 476113658Sdeischen &(*cond)->c_lock); 477113658Sdeischen 478113658Sdeischen /* Schedule the next thread: */ 479113658Sdeischen _thr_sched_switch(curthread); 480113658Sdeischen 48153812Salfred /* 482113658Sdeischen * XXX - This really isn't a good check 483113658Sdeischen * since there can be more than one 484113658Sdeischen * thread waiting on the CV. Signals 485113658Sdeischen * sent to threads waiting on mutexes 486113658Sdeischen * or CVs should really be deferred 487113658Sdeischen * until the threads are no longer 488113658Sdeischen * waiting, but POSIX says that signals 489113658Sdeischen * should be sent "as soon as possible". 49053812Salfred */ 491113658Sdeischen done = (seqno != (*cond)->c_seqno); 492139023Sdeischen if (done && !THR_IN_CONDQ(curthread)) { 49381918Sjasone /* 494139023Sdeischen * The thread is dequeued, so 495139023Sdeischen * it is safe to clear these. 49681918Sjasone */ 497139023Sdeischen curthread->data.cond = NULL; 498139023Sdeischen curthread->sigbackout = NULL; 499139023Sdeischen check_continuation(curthread, 500139023Sdeischen NULL, mutex); 501139023Sdeischen return (_mutex_cv_lock(mutex)); 502139023Sdeischen } 50344963Sjb 504139023Sdeischen /* Relock the CV structure: */ 505139023Sdeischen THR_LOCK_ACQUIRE(curthread, 506139023Sdeischen &(*cond)->c_lock); 507139023Sdeischen 508139023Sdeischen /* 509139023Sdeischen * Clear these after taking the lock to 510139023Sdeischen * prevent a race condition where a 511139023Sdeischen * signal can arrive before dequeueing 512139023Sdeischen * the thread. 513139023Sdeischen */ 514139023Sdeischen curthread->data.cond = NULL; 515139023Sdeischen curthread->sigbackout = NULL; 516139023Sdeischen 517139023Sdeischen done = (seqno != (*cond)->c_seqno); 518139023Sdeischen 519139023Sdeischen if (THR_IN_CONDQ(curthread)) { 52044963Sjb cond_queue_remove(*cond, 52171581Sdeischen curthread); 52244963Sjb 52344963Sjb /* Check for no more waiters: */ 52444963Sjb if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 52544963Sjb (*cond)->c_mutex = NULL; 526113658Sdeischen } 52744963Sjb 528113658Sdeischen if (curthread->timeout != 0) { 529113658Sdeischen /* The wait timedout. */ 530113658Sdeischen rval = ETIMEDOUT; 531117165Sdavidxu } 53217706Sjulian } 53313546Sjulian } 53417706Sjulian break; 53517706Sjulian 53624827Sjb /* Trap invalid condition variable types: */ 53717706Sjulian default: 53817706Sjulian /* Return an invalid argument error: */ 53931402Salex rval = EINVAL; 54017706Sjulian break; 54113546Sjulian } 54213546Sjulian 543139023Sdeischen check_continuation(curthread, *cond, 544139023Sdeischen mutex_locked ? NULL : mutex); 54568516Sdeischen } while ((done == 0) && (rval == 0)); 54613546Sjulian 547139023Sdeischen /* Unlock the condition variable structure: */ 548139023Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 549139023Sdeischen 550139023Sdeischen if (mutex_locked == 0) 551139023Sdeischen _mutex_cv_lock(mutex); 552139023Sdeischen 55313546Sjulian /* Return the completion status: */ 55413546Sjulian return (rval); 55513546Sjulian} 55613546Sjulian 55713546Sjulianint 558113658Sdeischen__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 559113658Sdeischen const struct timespec *abstime) 560113658Sdeischen{ 561113658Sdeischen struct pthread *curthread = _get_curthread(); 562113658Sdeischen int ret; 563113658Sdeischen 564123312Sdavidxu _thr_cancel_enter(curthread); 565113658Sdeischen ret = _pthread_cond_timedwait(cond, mutex, abstime); 566123312Sdavidxu _thr_cancel_leave(curthread, 1); 567113658Sdeischen return (ret); 568113658Sdeischen} 569113658Sdeischen 570113658Sdeischen 571113658Sdeischenint 57271581Sdeischen_pthread_cond_signal(pthread_cond_t * cond) 57313546Sjulian{ 574113658Sdeischen struct pthread *curthread = _get_curthread(); 575113658Sdeischen struct pthread *pthread; 576117907Sdeischen struct kse_mailbox *kmbx; 577113658Sdeischen int rval = 0; 57813546Sjulian 579113870Sdeischen THR_ASSERT(curthread->locklevel == 0, 580113870Sdeischen "cv_timedwait: locklevel is not zero!"); 58163355Sjasone if (cond == NULL) 58231402Salex rval = EINVAL; 58363355Sjasone /* 58463355Sjasone * If the condition variable is statically initialized, perform dynamic 58563355Sjasone * initialization. 58663355Sjasone */ 58768844Sdeischen else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) { 58835509Sjb /* Lock the condition variable structure: */ 589113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 59013546Sjulian 59117706Sjulian /* Process according to condition variable type: */ 59217706Sjulian switch ((*cond)->c_type) { 59324827Sjb /* Fast condition variable: */ 59417706Sjulian case COND_TYPE_FAST: 59568516Sdeischen /* Increment the sequence number: */ 59668516Sdeischen (*cond)->c_seqno++; 59768516Sdeischen 598113658Sdeischen /* 599113658Sdeischen * Wakeups have to be done with the CV lock held; 600113658Sdeischen * otherwise there is a race condition where the 601113658Sdeischen * thread can timeout, run on another KSE, and enter 602113658Sdeischen * another blocking state (including blocking on a CV). 603113658Sdeischen */ 604113658Sdeischen if ((pthread = TAILQ_FIRST(&(*cond)->c_queue)) 605113658Sdeischen != NULL) { 606113658Sdeischen THR_SCHED_LOCK(curthread, pthread); 607113658Sdeischen cond_queue_remove(*cond, pthread); 608139023Sdeischen pthread->sigbackout = NULL; 609117714Sdeischen if ((pthread->kseg == curthread->kseg) && 610117714Sdeischen (pthread->active_priority > 611117714Sdeischen curthread->active_priority)) 612117714Sdeischen curthread->critical_yield = 1; 613117907Sdeischen kmbx = _thr_setrunnable_unlocked(pthread); 614113658Sdeischen THR_SCHED_UNLOCK(curthread, pthread); 615117907Sdeischen if (kmbx != NULL) 616117907Sdeischen kse_wakeup(kmbx); 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 638115399Skan__strong_reference(_pthread_cond_signal, _thr_cond_signal); 639115399Skan 64013546Sjulianint 64171581Sdeischen_pthread_cond_broadcast(pthread_cond_t * cond) 64213546Sjulian{ 643113658Sdeischen struct pthread *curthread = _get_curthread(); 644113658Sdeischen struct pthread *pthread; 645117907Sdeischen struct kse_mailbox *kmbx; 646113658Sdeischen int rval = 0; 64713546Sjulian 648113870Sdeischen THR_ASSERT(curthread->locklevel == 0, 649113870Sdeischen "cv_timedwait: locklevel is not zero!"); 65063355Sjasone if (cond == NULL) 65135509Sjb rval = EINVAL; 65263355Sjasone /* 65363355Sjasone * If the condition variable is statically initialized, perform dynamic 65463355Sjasone * initialization. 65563355Sjasone */ 65668844Sdeischen else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) { 65735509Sjb /* Lock the condition variable structure: */ 658113658Sdeischen THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 65913546Sjulian 66035509Sjb /* Process according to condition variable type: */ 66135509Sjb switch ((*cond)->c_type) { 66235509Sjb /* Fast condition variable: */ 66335509Sjb case COND_TYPE_FAST: 66468516Sdeischen /* Increment the sequence number: */ 66568516Sdeischen (*cond)->c_seqno++; 66668516Sdeischen 66735509Sjb /* 66835509Sjb * Enter a loop to bring all threads off the 66935509Sjb * condition queue: 67035509Sjb */ 671113658Sdeischen while ((pthread = TAILQ_FIRST(&(*cond)->c_queue)) 672113658Sdeischen != NULL) { 673113658Sdeischen THR_SCHED_LOCK(curthread, pthread); 674113658Sdeischen cond_queue_remove(*cond, pthread); 675139023Sdeischen pthread->sigbackout = NULL; 676117714Sdeischen if ((pthread->kseg == curthread->kseg) && 677117714Sdeischen (pthread->active_priority > 678117714Sdeischen curthread->active_priority)) 679117714Sdeischen curthread->critical_yield = 1; 680117907Sdeischen kmbx = _thr_setrunnable_unlocked(pthread); 681113658Sdeischen THR_SCHED_UNLOCK(curthread, pthread); 682117907Sdeischen if (kmbx != NULL) 683117907Sdeischen kse_wakeup(kmbx); 68435509Sjb } 68544963Sjb 68644963Sjb /* There are no more waiting threads: */ 68744963Sjb (*cond)->c_mutex = NULL; 68835509Sjb break; 689115399Skan 69035509Sjb /* Trap invalid condition variable types: */ 69135509Sjb default: 69235509Sjb /* Return an invalid argument error: */ 69335509Sjb rval = EINVAL; 69435509Sjb break; 69513546Sjulian } 69613546Sjulian 69735509Sjb /* Unlock the condition variable structure: */ 698113658Sdeischen THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 69913546Sjulian } 70013546Sjulian 70113546Sjulian /* Return the completion status: */ 70213546Sjulian return (rval); 70313546Sjulian} 70444963Sjb 705115399Skan__strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast); 706115399Skan 707139023Sdeischenstatic inline void 708139023Sdeischencheck_continuation(struct pthread *curthread, struct pthread_cond *cond, 709139023Sdeischen pthread_mutex_t *mutex) 71067097Sdeischen{ 711139023Sdeischen if ((curthread->interrupted != 0) && 712139023Sdeischen (curthread->continuation != NULL)) { 713139023Sdeischen if (cond != NULL) 714139023Sdeischen /* Unlock the condition variable structure: */ 715139023Sdeischen THR_LOCK_RELEASE(curthread, &cond->c_lock); 716139023Sdeischen /* 717139023Sdeischen * Note that even though this thread may have been 718139023Sdeischen * canceled, POSIX requires that the mutex be 719139023Sdeischen * reaquired prior to cancellation. 720139023Sdeischen */ 721139023Sdeischen if (mutex != NULL) 722139023Sdeischen _mutex_cv_lock(mutex); 723139023Sdeischen curthread->continuation((void *) curthread); 724139023Sdeischen PANIC("continuation returned in pthread_cond_wait.\n"); 725139023Sdeischen } 726139023Sdeischen} 727139023Sdeischen 728139023Sdeischenstatic void 729139023Sdeischencond_wait_backout(void *arg) 730139023Sdeischen{ 731139023Sdeischen struct pthread *curthread = (struct pthread *)arg; 73267097Sdeischen pthread_cond_t cond; 73367097Sdeischen 734113658Sdeischen cond = curthread->data.cond; 73567097Sdeischen if (cond != NULL) { 73667097Sdeischen /* Lock the condition variable structure: */ 737113658Sdeischen THR_LOCK_ACQUIRE(curthread, &cond->c_lock); 73867097Sdeischen 73967097Sdeischen /* Process according to condition variable type: */ 74067097Sdeischen switch (cond->c_type) { 74167097Sdeischen /* Fast condition variable: */ 74267097Sdeischen case COND_TYPE_FAST: 743113658Sdeischen cond_queue_remove(cond, curthread); 74467097Sdeischen 74567097Sdeischen /* Check for no more waiters: */ 74667097Sdeischen if (TAILQ_FIRST(&cond->c_queue) == NULL) 74767097Sdeischen cond->c_mutex = NULL; 74867097Sdeischen break; 74967097Sdeischen 75067097Sdeischen default: 75167097Sdeischen break; 75267097Sdeischen } 75367097Sdeischen 75467097Sdeischen /* Unlock the condition variable structure: */ 755113658Sdeischen THR_LOCK_RELEASE(curthread, &cond->c_lock); 75667097Sdeischen } 757139023Sdeischen /* No need to call this again. */ 758139023Sdeischen curthread->sigbackout = NULL; 75967097Sdeischen} 76067097Sdeischen 76144963Sjb/* 76244963Sjb * Dequeue a waiting thread from the head of a condition queue in 76344963Sjb * descending priority order. 76444963Sjb */ 765113658Sdeischenstatic inline struct pthread * 76644963Sjbcond_queue_deq(pthread_cond_t cond) 76744963Sjb{ 768113658Sdeischen struct pthread *pthread; 76944963Sjb 77053812Salfred while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) { 77167097Sdeischen TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 772117165Sdavidxu THR_CONDQ_CLEAR(pthread); 77353812Salfred if ((pthread->timeout == 0) && (pthread->interrupted == 0)) 77453812Salfred /* 77553812Salfred * Only exit the loop when we find a thread 77653812Salfred * that hasn't timed out or been canceled; 77753812Salfred * those threads are already running and don't 77853812Salfred * need their run state changed. 77953812Salfred */ 78053812Salfred break; 78144963Sjb } 78244963Sjb 783113658Sdeischen return (pthread); 78444963Sjb} 78544963Sjb 78644963Sjb/* 78744963Sjb * Remove a waiting thread from a condition queue in descending priority 78844963Sjb * order. 78944963Sjb */ 79044963Sjbstatic inline void 791113658Sdeischencond_queue_remove(pthread_cond_t cond, struct pthread *pthread) 79244963Sjb{ 79344963Sjb /* 79444963Sjb * Because pthread_cond_timedwait() can timeout as well 79544963Sjb * as be signaled by another thread, it is necessary to 79644963Sjb * guard against removing the thread from the queue if 79744963Sjb * it isn't in the queue. 79844963Sjb */ 799113658Sdeischen if (THR_IN_CONDQ(pthread)) { 80067097Sdeischen TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 801113658Sdeischen THR_CONDQ_CLEAR(pthread); 80244963Sjb } 80344963Sjb} 80444963Sjb 80544963Sjb/* 80644963Sjb * Enqueue a waiting thread to a condition queue in descending priority 80744963Sjb * order. 80844963Sjb */ 80944963Sjbstatic inline void 810113658Sdeischencond_queue_enq(pthread_cond_t cond, struct pthread *pthread) 81144963Sjb{ 812113658Sdeischen struct pthread *tid = TAILQ_LAST(&cond->c_queue, cond_head); 81344963Sjb 814113658Sdeischen THR_ASSERT(!THR_IN_SYNCQ(pthread), 815113658Sdeischen "cond_queue_enq: thread already queued!"); 81667097Sdeischen 81744963Sjb /* 81844963Sjb * For the common case of all threads having equal priority, 81944963Sjb * we perform a quick check against the priority of the thread 82044963Sjb * at the tail of the queue. 82144963Sjb */ 82244963Sjb if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) 82367097Sdeischen TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe); 82444963Sjb else { 82544963Sjb tid = TAILQ_FIRST(&cond->c_queue); 82644963Sjb while (pthread->active_priority <= tid->active_priority) 82767097Sdeischen tid = TAILQ_NEXT(tid, sqe); 82867097Sdeischen TAILQ_INSERT_BEFORE(tid, pthread, sqe); 82944963Sjb } 830113658Sdeischen THR_CONDQ_SET(pthread); 83167097Sdeischen pthread->data.cond = cond; 83244963Sjb} 833