thr_cond.c revision 129484
1112918Sjeff/* 2112918Sjeff * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. 3112918Sjeff * All rights reserved. 4112918Sjeff * 5112918Sjeff * Redistribution and use in source and binary forms, with or without 6112918Sjeff * modification, are permitted provided that the following conditions 7112918Sjeff * are met: 8112918Sjeff * 1. Redistributions of source code must retain the above copyright 9112918Sjeff * notice, this list of conditions and the following disclaimer. 10112918Sjeff * 2. Redistributions in binary form must reproduce the above copyright 11112918Sjeff * notice, this list of conditions and the following disclaimer in the 12112918Sjeff * documentation and/or other materials provided with the distribution. 13112918Sjeff * 3. All advertising materials mentioning features or use of this software 14112918Sjeff * must display the following acknowledgement: 15112918Sjeff * This product includes software developed by John Birrell. 16112918Sjeff * 4. Neither the name of the author nor the names of any co-contributors 17112918Sjeff * may be used to endorse or promote products derived from this software 18112918Sjeff * without specific prior written permission. 19112918Sjeff * 20112918Sjeff * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 21112918Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22112918Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23112918Sjeff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24112918Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25112918Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26112918Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27112918Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28112918Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29112918Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30112918Sjeff * SUCH DAMAGE. 31112918Sjeff * 32112918Sjeff * $FreeBSD: head/lib/libthr/thread/thr_cond.c 129484 2004-05-20 12:06:16Z mtm $ 33112918Sjeff */ 34112918Sjeff#include <stdlib.h> 35112918Sjeff#include <errno.h> 36112918Sjeff#include <string.h> 37112918Sjeff#include <pthread.h> 38112918Sjeff#include "thr_private.h" 39112918Sjeff 40112918Sjeff/* 41115389Smtm * Proctect two different threads calling a pthread_cond_* function 42115389Smtm * from accidentally initializing the condition variable twice. 43115389Smtm */ 44115389Smtmstatic spinlock_t static_cond_lock = _SPINLOCK_INITIALIZER; 45115389Smtm 46115389Smtm/* 47112918Sjeff * Prototypes 48112918Sjeff */ 49115389Smtmstatic inline int cond_init(pthread_cond_t *); 50112918Sjeffstatic pthread_t cond_queue_deq(pthread_cond_t); 51112918Sjeffstatic void cond_queue_remove(pthread_cond_t, pthread_t); 52112918Sjeffstatic void cond_queue_enq(pthread_cond_t, pthread_t); 53115277Smtmstatic int cond_signal(pthread_cond_t *, int); 54115035Smtmstatic int cond_wait_common(pthread_cond_t *, 55115035Smtm pthread_mutex_t *, const struct timespec *); 56112918Sjeff 57112918Sjeff__weak_reference(_pthread_cond_init, pthread_cond_init); 58112918Sjeff__weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 59112918Sjeff__weak_reference(_pthread_cond_wait, pthread_cond_wait); 60112918Sjeff__weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait); 61112918Sjeff__weak_reference(_pthread_cond_signal, pthread_cond_signal); 62112918Sjeff__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 63112918Sjeff 64112918Sjeff#define COND_LOCK(c) \ 65112918Sjeffdo { \ 66112918Sjeff if (umtx_lock(&(c)->c_lock, curthread->thr_id)) \ 67112918Sjeff abort(); \ 68112918Sjeff} while (0) 69112918Sjeff 70112918Sjeff#define COND_UNLOCK(c) \ 71112918Sjeffdo { \ 72112918Sjeff if (umtx_unlock(&(c)->c_lock, curthread->thr_id)) \ 73112918Sjeff abort(); \ 74112918Sjeff} while (0) 75112918Sjeff 76112918Sjeff 77112918Sjeff/* Reinitialize a condition variable to defaults. */ 78112918Sjeffint 79112918Sjeff_cond_reinit(pthread_cond_t *cond) 80112918Sjeff{ 81112918Sjeff if (cond == NULL) 82112918Sjeff return (EINVAL); 83112918Sjeff 84112918Sjeff if (*cond == NULL) 85112918Sjeff return (pthread_cond_init(cond, NULL)); 86112918Sjeff 87112918Sjeff /* 88112918Sjeff * Initialize the condition variable structure: 89112918Sjeff */ 90112918Sjeff TAILQ_INIT(&(*cond)->c_queue); 91112918Sjeff (*cond)->c_flags = COND_FLAGS_INITED; 92112918Sjeff (*cond)->c_type = COND_TYPE_FAST; 93112918Sjeff (*cond)->c_mutex = NULL; 94112918Sjeff (*cond)->c_seqno = 0; 95112918Sjeff bzero(&(*cond)->c_lock, sizeof((*cond)->c_lock)); 96112918Sjeff 97112918Sjeff return (0); 98112918Sjeff} 99112918Sjeff 100112918Sjeffint 101112918Sjeff_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 102112918Sjeff{ 103112918Sjeff enum pthread_cond_type type; 104112918Sjeff pthread_cond_t pcond; 105112918Sjeff 106112918Sjeff if (cond == NULL) 107112918Sjeff return (EINVAL); 108112918Sjeff 109112918Sjeff /* 110112918Sjeff * Check if a pointer to a condition variable attribute 111112918Sjeff * structure was passed by the caller: 112112918Sjeff */ 113112918Sjeff if (cond_attr != NULL && *cond_attr != NULL) 114112918Sjeff type = (*cond_attr)->c_type; 115112918Sjeff else 116112918Sjeff /* Default to a fast condition variable: */ 117112918Sjeff type = COND_TYPE_FAST; 118112918Sjeff 119112918Sjeff /* Process according to condition variable type: */ 120112918Sjeff switch (type) { 121112918Sjeff case COND_TYPE_FAST: 122112918Sjeff break; 123112918Sjeff default: 124112918Sjeff return (EINVAL); 125112918Sjeff break; 126112918Sjeff } 127112918Sjeff 128112918Sjeff if ((pcond = (pthread_cond_t) 129112918Sjeff malloc(sizeof(struct pthread_cond))) == NULL) 130112918Sjeff return (ENOMEM); 131112918Sjeff /* 132112918Sjeff * Initialise the condition variable 133112918Sjeff * structure: 134112918Sjeff */ 135112918Sjeff TAILQ_INIT(&pcond->c_queue); 136112918Sjeff pcond->c_flags |= COND_FLAGS_INITED; 137112918Sjeff pcond->c_type = type; 138112918Sjeff pcond->c_mutex = NULL; 139112918Sjeff pcond->c_seqno = 0; 140112918Sjeff bzero(&pcond->c_lock, sizeof(pcond->c_lock)); 141112918Sjeff 142112918Sjeff *cond = pcond; 143112918Sjeff 144112918Sjeff return (0); 145112918Sjeff} 146112918Sjeff 147112918Sjeffint 148112918Sjeff_pthread_cond_destroy(pthread_cond_t *cond) 149112918Sjeff{ 150127566Smtm /* 151127566Smtm * Short circuit for a statically initialized condvar 152127566Smtm * that is being destroyed without having been used. 153127566Smtm */ 154127566Smtm if (*cond == PTHREAD_COND_INITIALIZER) 155127566Smtm return (0); 156112918Sjeff 157112918Sjeff COND_LOCK(*cond); 158112918Sjeff 159112918Sjeff /* 160112918Sjeff * Free the memory allocated for the condition 161112918Sjeff * variable structure: 162112918Sjeff */ 163112918Sjeff free(*cond); 164112918Sjeff 165112918Sjeff /* 166112918Sjeff * NULL the caller's pointer now that the condition 167112918Sjeff * variable has been destroyed: 168112918Sjeff */ 169112918Sjeff *cond = NULL; 170112918Sjeff 171112918Sjeff return (0); 172112918Sjeff} 173112918Sjeff 174112918Sjeffint 175112918Sjeff_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 176112918Sjeff{ 177112918Sjeff int rval; 178112918Sjeff 179115035Smtm rval = cond_wait_common(cond, mutex, NULL); 180112918Sjeff 181112918Sjeff /* This should never happen. */ 182112918Sjeff if (rval == ETIMEDOUT) 183112918Sjeff abort(); 184112918Sjeff 185112918Sjeff return (rval); 186112918Sjeff} 187112918Sjeff 188112918Sjeffint 189112918Sjeff_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 190112918Sjeff const struct timespec * abstime) 191112918Sjeff{ 192115035Smtm if (abstime == NULL || abstime->tv_nsec >= 1000000000) 193115035Smtm return (EINVAL); 194115035Smtm 195115035Smtm return (cond_wait_common(cond, mutex, abstime)); 196115035Smtm} 197115035Smtm 198115035Smtmstatic int 199115035Smtmcond_wait_common(pthread_cond_t * cond, pthread_mutex_t * mutex, 200115035Smtm const struct timespec * abstime) 201115035Smtm{ 202112918Sjeff int rval = 0; 203112918Sjeff int mtxrval; 204112918Sjeff 205112918Sjeff 206115277Smtm if (cond == NULL) 207115277Smtm return (EINVAL); 208112918Sjeff /* 209112918Sjeff * If the condition variable is statically initialized, perform dynamic 210112918Sjeff * initialization. 211112918Sjeff */ 212115389Smtm if (*cond == PTHREAD_COND_INITIALIZER && (rval = cond_init(cond)) != 0) 213112918Sjeff return (rval); 214112918Sjeff 215129484Smtm if ((*cond)->c_type != COND_TYPE_FAST) 216129484Smtm return (EINVAL); 217112918Sjeff 218112918Sjeff COND_LOCK(*cond); 219112918Sjeff 220112918Sjeff /* 221112918Sjeff * If the condvar was statically allocated, properly 222112918Sjeff * initialize the tail queue. 223112918Sjeff */ 224112918Sjeff if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 225112918Sjeff TAILQ_INIT(&(*cond)->c_queue); 226112918Sjeff (*cond)->c_flags |= COND_FLAGS_INITED; 227112918Sjeff } 228112918Sjeff 229129484Smtm if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 230129484Smtm ((*cond)->c_mutex != *mutex))) { 231129484Smtm COND_UNLOCK(*cond); 232129484Smtm return (EINVAL); 233129484Smtm } 234129484Smtm /* Remember the mutex */ 235129484Smtm (*cond)->c_mutex = *mutex; 236112918Sjeff 237129484Smtm _thread_enter_cancellation_point(); 238129484Smtm if ((rval = _mutex_cv_unlock(mutex)) != 0) { 239129484Smtm if (rval == -1){ 240129484Smtm printf("mutex unlock by condvar failed!"); 241129484Smtm fflush(stdout); 242129484Smtm abort(); 243112918Sjeff } 244129484Smtm _thread_leave_cancellation_point(); 245129484Smtm COND_UNLOCK(*cond); 246129484Smtm return (rval); 247129484Smtm } 248112918Sjeff 249129484Smtm /* 250129484Smtm * We need to protect the queue operations. It also 251129484Smtm * protects the pthread flag field. This is 252129484Smtm * dropped before calling _thread_suspend() and reaquired 253129484Smtm * when we return. 254129484Smtm */ 255129484Smtm PTHREAD_LOCK(curthread); 256112918Sjeff 257129484Smtm /* 258129484Smtm * Queue the running thread on the condition 259129484Smtm * variable and wait to be signaled. 260129484Smtm */ 261129484Smtm cond_queue_enq(*cond, curthread); 262129484Smtm do { 263129484Smtm PTHREAD_UNLOCK(curthread); 264129484Smtm COND_UNLOCK(*cond); 265129484Smtm if (curthread->cancellation == CS_PENDING) { 266112918Sjeff /* 267129484Smtm * Posix says we must lock the mutex 268129484Smtm * even if we're being canceled. 269112918Sjeff */ 270129484Smtm _mutex_cv_lock(mutex); 271129484Smtm _thread_leave_cancellation_point(); 272129484Smtm PANIC("Shouldn't have come back."); 273129484Smtm } 274129484Smtm rval = _thread_suspend(curthread, (struct timespec *)abstime); 275129484Smtm if (rval != 0 && rval != ETIMEDOUT && rval != EINTR) { 276129484Smtm printf("thread suspend returned an invalid value"); 277129484Smtm fflush(stdout); 278129484Smtm abort(); 279129484Smtm } 280129484Smtm COND_LOCK(*cond); 281129484Smtm PTHREAD_LOCK(curthread); 282129484Smtm if (rval == ETIMEDOUT) { 283115277Smtm /* 284129484Smtm * Condition may have been signaled between the 285129484Smtm * time the thread timed out and locked the condvar. 286129484Smtm * If it wasn't, manually remove it from the queue. 287115277Smtm */ 288129484Smtm if ((curthread->flags & PTHREAD_FLAGS_IN_CONDQ) == 0) 289112918Sjeff rval = 0; 290129484Smtm else 291129484Smtm cond_queue_remove(*cond, curthread); 292112918Sjeff } 293129484Smtm } while ((curthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0); 294112918Sjeff 295129484Smtm PTHREAD_UNLOCK(curthread); 296129484Smtm COND_UNLOCK(*cond); 297129484Smtm mtxrval = _mutex_cv_lock(mutex); 298112918Sjeff 299129484Smtm /* If the mutex failed return that error. */ 300129484Smtm if (mtxrval == -1) { 301129484Smtm printf("mutex lock from condvar failed!"); 302129484Smtm fflush(stdout); 303129484Smtm abort(); 304112918Sjeff } 305129484Smtm if (mtxrval != 0) 306129484Smtm rval = mtxrval; 307112918Sjeff 308112918Sjeff _thread_leave_cancellation_point(); 309112918Sjeff return (rval); 310112918Sjeff} 311112918Sjeff 312112918Sjeffint 313112918Sjeff_pthread_cond_signal(pthread_cond_t * cond) 314112918Sjeff{ 315115277Smtm return (cond_signal(cond, 0)); 316112918Sjeff} 317112918Sjeff 318112918Sjeffint 319112918Sjeff_pthread_cond_broadcast(pthread_cond_t * cond) 320112918Sjeff{ 321115277Smtm return (cond_signal(cond, 1)); 322115277Smtm} 323115277Smtm 324115277Smtmstatic int 325115277Smtmcond_signal(pthread_cond_t * cond, int broadcast) 326115277Smtm{ 327112918Sjeff int rval = 0; 328112918Sjeff pthread_t pthread; 329112918Sjeff 330112918Sjeff if (cond == NULL) 331112918Sjeff return (EINVAL); 332112918Sjeff /* 333112918Sjeff * If the condition variable is statically initialized, perform dynamic 334112918Sjeff * initialization. 335112918Sjeff */ 336115389Smtm if (*cond == PTHREAD_COND_INITIALIZER && (rval = cond_init(cond)) != 0) 337112918Sjeff return (rval); 338112918Sjeff 339129484Smtm if ((*cond)->c_type != COND_TYPE_FAST) 340129484Smtm return (EINVAL); 341112918Sjeff COND_LOCK(*cond); 342112918Sjeff 343129484Smtm /* 344129484Smtm * Enter a loop to bring all (or only one) threads off the 345129484Smtm * condition queue: 346129484Smtm */ 347129484Smtm do { 348112918Sjeff /* 349129484Smtm * Wake up the signaled thread. It will be returned 350129484Smtm * to us locked. 351112918Sjeff */ 352129484Smtm if ((pthread = cond_queue_deq(*cond)) != NULL) { 353129484Smtm PTHREAD_WAKE(pthread); 354129484Smtm PTHREAD_UNLOCK(pthread); 355129484Smtm } 356129484Smtm } while (broadcast && pthread != NULL); 357112918Sjeff 358112918Sjeff COND_UNLOCK(*cond); 359112918Sjeff return (rval); 360112918Sjeff} 361112918Sjeff 362112918Sjeffvoid 363112918Sjeff_cond_wait_backout(pthread_t pthread) 364112918Sjeff{ 365112918Sjeff pthread_cond_t cond; 366112918Sjeff 367112918Sjeff cond = pthread->data.cond; 368112918Sjeff if (cond == NULL) 369112918Sjeff return; 370112918Sjeff 371112918Sjeff /* Process according to condition variable type: */ 372112918Sjeff switch (cond->c_type) { 373112918Sjeff /* Fast condition variable: */ 374112918Sjeff case COND_TYPE_FAST: 375112918Sjeff cond_queue_remove(cond, pthread); 376112918Sjeff break; 377112918Sjeff default: 378112918Sjeff break; 379112918Sjeff } 380112918Sjeff} 381112918Sjeff 382112918Sjeff/* 383112918Sjeff * Dequeue a waiting thread from the head of a condition queue in 384112918Sjeff * descending priority order. 385112918Sjeff */ 386112918Sjeffstatic pthread_t 387112918Sjeffcond_queue_deq(pthread_cond_t cond) 388112918Sjeff{ 389112918Sjeff pthread_t pthread; 390112918Sjeff 391112918Sjeff while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) { 392129484Smtm PTHREAD_LOCK(pthread); 393112918Sjeff cond_queue_remove(cond, pthread); 394129484Smtm 395129484Smtm /* 396129484Smtm * Only exit the loop when we find a thread 397129484Smtm * that hasn't been canceled. 398129484Smtm */ 399129484Smtm if (pthread->cancellation == CS_NULL) 400112918Sjeff break; 401115277Smtm else 402129484Smtm PTHREAD_UNLOCK(pthread); 403112918Sjeff } 404112918Sjeff 405112918Sjeff return(pthread); 406112918Sjeff} 407112918Sjeff 408112918Sjeff/* 409112918Sjeff * Remove a waiting thread from a condition queue in descending priority 410112918Sjeff * order. 411112918Sjeff */ 412112918Sjeffstatic void 413112918Sjeffcond_queue_remove(pthread_cond_t cond, pthread_t pthread) 414112918Sjeff{ 415112918Sjeff /* 416112918Sjeff * Because pthread_cond_timedwait() can timeout as well 417112918Sjeff * as be signaled by another thread, it is necessary to 418112918Sjeff * guard against removing the thread from the queue if 419112918Sjeff * it isn't in the queue. 420112918Sjeff */ 421112918Sjeff if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) { 422112918Sjeff TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 423112918Sjeff pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ; 424112918Sjeff } 425112918Sjeff /* Check for no more waiters. */ 426112918Sjeff if (TAILQ_FIRST(&cond->c_queue) == NULL) 427112918Sjeff cond->c_mutex = NULL; 428112918Sjeff} 429112918Sjeff 430112918Sjeff/* 431112918Sjeff * Enqueue a waiting thread to a condition queue in descending priority 432112918Sjeff * order. 433112918Sjeff */ 434112918Sjeffstatic void 435112918Sjeffcond_queue_enq(pthread_cond_t cond, pthread_t pthread) 436112918Sjeff{ 437112918Sjeff pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head); 438115198Smtm char *name; 439112918Sjeff 440115198Smtm name = pthread->name ? pthread->name : "unknown"; 441115198Smtm if ((pthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0) 442115198Smtm _thread_printf(2, "Thread (%s:%u) already on condq\n", 443115198Smtm pthread->name, pthread->uniqueid); 444115198Smtm if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) 445115198Smtm _thread_printf(2, "Thread (%s:%u) already on mutexq\n", 446115198Smtm pthread->name, pthread->uniqueid); 447112918Sjeff PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread); 448112918Sjeff 449112918Sjeff /* 450112918Sjeff * For the common case of all threads having equal priority, 451112918Sjeff * we perform a quick check against the priority of the thread 452112918Sjeff * at the tail of the queue. 453112918Sjeff */ 454112918Sjeff if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) 455112918Sjeff TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe); 456112918Sjeff else { 457112918Sjeff tid = TAILQ_FIRST(&cond->c_queue); 458112918Sjeff while (pthread->active_priority <= tid->active_priority) 459112918Sjeff tid = TAILQ_NEXT(tid, sqe); 460112918Sjeff TAILQ_INSERT_BEFORE(tid, pthread, sqe); 461112918Sjeff } 462112918Sjeff pthread->flags |= PTHREAD_FLAGS_IN_CONDQ; 463112918Sjeff pthread->data.cond = cond; 464112918Sjeff} 465115389Smtm 466115389Smtmstatic inline int 467115389Smtmcond_init(pthread_cond_t *cond) 468115389Smtm{ 469115442Smtm int error = 0; 470115389Smtm _SPINLOCK(&static_cond_lock); 471115389Smtm if (*cond == PTHREAD_COND_INITIALIZER) 472115442Smtm error = _pthread_cond_init(cond, NULL); 473115389Smtm _SPINUNLOCK(&static_cond_lock); 474115442Smtm return (error); 475115389Smtm} 476115389Smtm 477