thr_cond.c revision 211524
1112918Sjeff/* 2144518Sdavidxu * Copyright (c) 2005 David Xu <davidxu@freebsd.org> 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 9144518Sdavidxu * notice unmodified, this list of conditions, and the following 10144518Sdavidxu * disclaimer. 11112918Sjeff * 2. Redistributions in binary form must reproduce the above copyright 12112918Sjeff * notice, this list of conditions and the following disclaimer in the 13112918Sjeff * documentation and/or other materials provided with the distribution. 14112918Sjeff * 15144518Sdavidxu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16144518Sdavidxu * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17144518Sdavidxu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18144518Sdavidxu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19144518Sdavidxu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20144518Sdavidxu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21144518Sdavidxu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22144518Sdavidxu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23144518Sdavidxu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24144518Sdavidxu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25112918Sjeff * 26112918Sjeff * $FreeBSD: head/lib/libthr/thread/thr_cond.c 211524 2010-08-20 05:15:39Z davidxu $ 27112918Sjeff */ 28144518Sdavidxu 29157457Sdavidxu#include "namespace.h" 30112918Sjeff#include <stdlib.h> 31112918Sjeff#include <errno.h> 32112918Sjeff#include <string.h> 33112918Sjeff#include <pthread.h> 34144518Sdavidxu#include <limits.h> 35157457Sdavidxu#include "un-namespace.h" 36144518Sdavidxu 37112918Sjeff#include "thr_private.h" 38112918Sjeff 39112918Sjeff/* 40144518Sdavidxu * Prototypes 41115389Smtm */ 42157457Sdavidxuint __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 43157457Sdavidxuint __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 44157457Sdavidxu const struct timespec * abstime); 45144518Sdavidxustatic int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); 46144518Sdavidxustatic int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 47144518Sdavidxu const struct timespec *abstime, int cancel); 48144518Sdavidxustatic int cond_signal_common(pthread_cond_t *cond, int broadcast); 49115389Smtm 50115389Smtm/* 51144518Sdavidxu * Double underscore versions are cancellation points. Single underscore 52144518Sdavidxu * versions are not and are provided for libc internal usage (which 53144518Sdavidxu * shouldn't introduce cancellation points). 54112918Sjeff */ 55144518Sdavidxu__weak_reference(__pthread_cond_wait, pthread_cond_wait); 56144518Sdavidxu__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 57112918Sjeff 58112918Sjeff__weak_reference(_pthread_cond_init, pthread_cond_init); 59112918Sjeff__weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 60112918Sjeff__weak_reference(_pthread_cond_signal, pthread_cond_signal); 61112918Sjeff__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 62112918Sjeff 63144518Sdavidxustatic int 64144518Sdavidxucond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 65144518Sdavidxu{ 66144518Sdavidxu pthread_cond_t pcond; 67144518Sdavidxu int rval = 0; 68112918Sjeff 69144518Sdavidxu if ((pcond = (pthread_cond_t) 70164877Sdavidxu calloc(1, sizeof(struct pthread_cond))) == NULL) { 71144518Sdavidxu rval = ENOMEM; 72144518Sdavidxu } else { 73144518Sdavidxu /* 74144518Sdavidxu * Initialise the condition variable structure: 75144518Sdavidxu */ 76144518Sdavidxu if (cond_attr == NULL || *cond_attr == NULL) { 77144518Sdavidxu pcond->c_pshared = 0; 78144518Sdavidxu pcond->c_clockid = CLOCK_REALTIME; 79144518Sdavidxu } else { 80144518Sdavidxu pcond->c_pshared = (*cond_attr)->c_pshared; 81144518Sdavidxu pcond->c_clockid = (*cond_attr)->c_clockid; 82144518Sdavidxu } 83164877Sdavidxu _thr_umutex_init(&pcond->c_lock); 84144518Sdavidxu *cond = pcond; 85144518Sdavidxu } 86144518Sdavidxu /* Return the completion status: */ 87144518Sdavidxu return (rval); 88144518Sdavidxu} 89112918Sjeff 90144518Sdavidxustatic int 91144518Sdavidxuinit_static(struct pthread *thread, pthread_cond_t *cond) 92112918Sjeff{ 93144518Sdavidxu int ret; 94112918Sjeff 95144518Sdavidxu THR_LOCK_ACQUIRE(thread, &_cond_static_lock); 96144518Sdavidxu 97112918Sjeff if (*cond == NULL) 98144518Sdavidxu ret = cond_init(cond, NULL); 99144518Sdavidxu else 100144518Sdavidxu ret = 0; 101112918Sjeff 102144518Sdavidxu THR_LOCK_RELEASE(thread, &_cond_static_lock); 103112918Sjeff 104144518Sdavidxu return (ret); 105112918Sjeff} 106112918Sjeff 107112918Sjeffint 108112918Sjeff_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 109112918Sjeff{ 110112918Sjeff 111144518Sdavidxu *cond = NULL; 112144518Sdavidxu return (cond_init(cond, cond_attr)); 113112918Sjeff} 114112918Sjeff 115112918Sjeffint 116112918Sjeff_pthread_cond_destroy(pthread_cond_t *cond) 117112918Sjeff{ 118164877Sdavidxu struct pthread *curthread = _get_curthread(); 119144518Sdavidxu struct pthread_cond *cv; 120144518Sdavidxu int rval = 0; 121112918Sjeff 122144518Sdavidxu if (*cond == NULL) 123144518Sdavidxu rval = EINVAL; 124144518Sdavidxu else { 125164877Sdavidxu cv = *cond; 126164877Sdavidxu THR_UMUTEX_LOCK(curthread, &cv->c_lock); 127144518Sdavidxu /* 128144518Sdavidxu * NULL the caller's pointer now that the condition 129144518Sdavidxu * variable has been destroyed: 130144518Sdavidxu */ 131144518Sdavidxu *cond = NULL; 132164877Sdavidxu THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 133112918Sjeff 134144518Sdavidxu /* 135144518Sdavidxu * Free the memory allocated for the condition 136144518Sdavidxu * variable structure: 137144518Sdavidxu */ 138144518Sdavidxu free(cv); 139144518Sdavidxu } 140144518Sdavidxu /* Return the completion status: */ 141112918Sjeff return (rval); 142112918Sjeff} 143112918Sjeff 144144518Sdavidxustruct cond_cancel_info 145112918Sjeff{ 146144518Sdavidxu pthread_mutex_t *mutex; 147144518Sdavidxu pthread_cond_t *cond; 148157591Sdavidxu int count; 149144518Sdavidxu}; 150115035Smtm 151144518Sdavidxustatic void 152144518Sdavidxucond_cancel_handler(void *arg) 153144518Sdavidxu{ 154144518Sdavidxu struct pthread *curthread = _get_curthread(); 155157591Sdavidxu struct cond_cancel_info *info = (struct cond_cancel_info *)arg; 156164877Sdavidxu pthread_cond_t cv; 157144518Sdavidxu 158165110Sdavidxu if (info->cond != NULL) { 159165110Sdavidxu cv = *(info->cond); 160164877Sdavidxu THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 161165110Sdavidxu } 162157591Sdavidxu _mutex_cv_lock(info->mutex, info->count); 163115035Smtm} 164115035Smtm 165211524Sdavidxu/* 166211524Sdavidxu * Cancellation behaivor: 167211524Sdavidxu * Thread may be canceled at start, if thread is canceled, it means it 168211524Sdavidxu * did not get a wakeup from pthread_cond_signal(), otherwise, it is 169211524Sdavidxu * not canceled. 170211524Sdavidxu * Thread cancellation never cause wakeup from pthread_cond_signal() 171211524Sdavidxu * to be lost. 172211524Sdavidxu */ 173115035Smtmstatic int 174144518Sdavidxucond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 175144518Sdavidxu const struct timespec *abstime, int cancel) 176115035Smtm{ 177144518Sdavidxu struct pthread *curthread = _get_curthread(); 178144518Sdavidxu struct timespec ts, ts2, *tsp; 179157591Sdavidxu struct cond_cancel_info info; 180144518Sdavidxu pthread_cond_t cv; 181144518Sdavidxu int ret = 0; 182112918Sjeff 183112918Sjeff /* 184144518Sdavidxu * If the condition variable is statically initialized, 185144518Sdavidxu * perform the dynamic initialization: 186112918Sjeff */ 187144518Sdavidxu if (__predict_false(*cond == NULL && 188144518Sdavidxu (ret = init_static(curthread, cond)) != 0)) 189144518Sdavidxu return (ret); 190112918Sjeff 191211524Sdavidxu _thr_testcancel(curthread); 192211524Sdavidxu 193144518Sdavidxu cv = *cond; 194164877Sdavidxu THR_UMUTEX_LOCK(curthread, &cv->c_lock); 195157591Sdavidxu ret = _mutex_cv_unlock(mutex, &info.count); 196144518Sdavidxu if (ret) { 197164877Sdavidxu THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 198144518Sdavidxu return (ret); 199112918Sjeff } 200164877Sdavidxu 201157591Sdavidxu info.mutex = mutex; 202157591Sdavidxu info.cond = cond; 203112918Sjeff 204164877Sdavidxu if (abstime != NULL) { 205164877Sdavidxu clock_gettime(cv->c_clockid, &ts); 206164877Sdavidxu TIMESPEC_SUB(&ts2, abstime, &ts); 207164877Sdavidxu tsp = &ts2; 208164877Sdavidxu } else 209164877Sdavidxu tsp = NULL; 210112918Sjeff 211164877Sdavidxu if (cancel) { 212164877Sdavidxu THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); 213211524Sdavidxu _thr_cancel_enter_defer(curthread, 0); 214164877Sdavidxu ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 1); 215165110Sdavidxu info.cond = NULL; 216211524Sdavidxu _thr_cancel_leave_defer(curthread, (ret != 0)); 217164877Sdavidxu THR_CLEANUP_POP(curthread, 0); 218144518Sdavidxu } else { 219164877Sdavidxu ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 0); 220112918Sjeff } 221164877Sdavidxu if (ret == EINTR) 222164877Sdavidxu ret = 0; 223157591Sdavidxu _mutex_cv_lock(mutex, info.count); 224144518Sdavidxu return (ret); 225112918Sjeff} 226112918Sjeff 227112918Sjeffint 228144518Sdavidxu_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 229112918Sjeff{ 230144518Sdavidxu 231144518Sdavidxu return (cond_wait_common(cond, mutex, NULL, 0)); 232112918Sjeff} 233112918Sjeff 234112918Sjeffint 235144518Sdavidxu__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 236112918Sjeff{ 237144518Sdavidxu 238144518Sdavidxu return (cond_wait_common(cond, mutex, NULL, 1)); 239115277Smtm} 240115277Smtm 241144518Sdavidxuint 242144518Sdavidxu_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 243144518Sdavidxu const struct timespec * abstime) 244115277Smtm{ 245112918Sjeff 246144518Sdavidxu if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 247144518Sdavidxu abstime->tv_nsec >= 1000000000) 248112918Sjeff return (EINVAL); 249112918Sjeff 250144518Sdavidxu return (cond_wait_common(cond, mutex, abstime, 0)); 251112918Sjeff} 252112918Sjeff 253144518Sdavidxuint 254144518Sdavidxu__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 255144518Sdavidxu const struct timespec *abstime) 256112918Sjeff{ 257112918Sjeff 258144518Sdavidxu if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 259144518Sdavidxu abstime->tv_nsec >= 1000000000) 260144518Sdavidxu return (EINVAL); 261112918Sjeff 262144518Sdavidxu return (cond_wait_common(cond, mutex, abstime, 1)); 263112918Sjeff} 264112918Sjeff 265144518Sdavidxustatic int 266144518Sdavidxucond_signal_common(pthread_cond_t *cond, int broadcast) 267112918Sjeff{ 268144518Sdavidxu struct pthread *curthread = _get_curthread(); 269144518Sdavidxu pthread_cond_t cv; 270164877Sdavidxu int ret = 0; 271112918Sjeff 272112918Sjeff /* 273144518Sdavidxu * If the condition variable is statically initialized, perform dynamic 274144518Sdavidxu * initialization. 275112918Sjeff */ 276144518Sdavidxu if (__predict_false(*cond == NULL && 277144518Sdavidxu (ret = init_static(curthread, cond)) != 0)) 278144518Sdavidxu return (ret); 279144518Sdavidxu 280144518Sdavidxu cv = *cond; 281164877Sdavidxu THR_UMUTEX_LOCK(curthread, &cv->c_lock); 282165110Sdavidxu if (!broadcast) 283165110Sdavidxu ret = _thr_ucond_signal(&cv->c_kerncv); 284165110Sdavidxu else 285165110Sdavidxu ret = _thr_ucond_broadcast(&cv->c_kerncv); 286164877Sdavidxu THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 287144518Sdavidxu return (ret); 288112918Sjeff} 289112918Sjeff 290144518Sdavidxuint 291144518Sdavidxu_pthread_cond_signal(pthread_cond_t * cond) 292112918Sjeff{ 293112918Sjeff 294144518Sdavidxu return (cond_signal_common(cond, 0)); 295112918Sjeff} 296115389Smtm 297144518Sdavidxuint 298144518Sdavidxu_pthread_cond_broadcast(pthread_cond_t * cond) 299115389Smtm{ 300144518Sdavidxu 301144518Sdavidxu return (cond_signal_common(cond, 1)); 302115389Smtm} 303