thr_cond.c revision 144518
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 144518 2005-04-02 01:20:00Z davidxu $ 27112918Sjeff */ 28144518Sdavidxu 29112918Sjeff#include <stdlib.h> 30112918Sjeff#include <errno.h> 31112918Sjeff#include <string.h> 32112918Sjeff#include <pthread.h> 33144518Sdavidxu#include <limits.h> 34144518Sdavidxu 35112918Sjeff#include "thr_private.h" 36112918Sjeff 37112918Sjeff/* 38144518Sdavidxu * Prototypes 39115389Smtm */ 40144518Sdavidxustatic int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); 41144518Sdavidxustatic int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 42144518Sdavidxu const struct timespec *abstime, int cancel); 43144518Sdavidxustatic int cond_signal_common(pthread_cond_t *cond, int broadcast); 44115389Smtm 45115389Smtm/* 46144518Sdavidxu * Double underscore versions are cancellation points. Single underscore 47144518Sdavidxu * versions are not and are provided for libc internal usage (which 48144518Sdavidxu * shouldn't introduce cancellation points). 49112918Sjeff */ 50144518Sdavidxu__weak_reference(__pthread_cond_wait, pthread_cond_wait); 51144518Sdavidxu__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 52112918Sjeff 53112918Sjeff__weak_reference(_pthread_cond_init, pthread_cond_init); 54112918Sjeff__weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 55112918Sjeff__weak_reference(_pthread_cond_signal, pthread_cond_signal); 56112918Sjeff__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 57112918Sjeff 58144518Sdavidxustatic int 59144518Sdavidxucond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 60144518Sdavidxu{ 61144518Sdavidxu pthread_cond_t pcond; 62144518Sdavidxu int rval = 0; 63112918Sjeff 64144518Sdavidxu if ((pcond = (pthread_cond_t) 65144518Sdavidxu malloc(sizeof(struct pthread_cond))) == NULL) { 66144518Sdavidxu rval = ENOMEM; 67144518Sdavidxu } else { 68144518Sdavidxu /* 69144518Sdavidxu * Initialise the condition variable structure: 70144518Sdavidxu */ 71144518Sdavidxu _thr_umtx_init(&pcond->c_lock); 72144518Sdavidxu pcond->c_seqno = 0; 73144518Sdavidxu pcond->c_waiters = 0; 74144518Sdavidxu pcond->c_wakeups = 0; 75144518Sdavidxu if (cond_attr == NULL || *cond_attr == NULL) { 76144518Sdavidxu pcond->c_pshared = 0; 77144518Sdavidxu pcond->c_clockid = CLOCK_REALTIME; 78144518Sdavidxu } else { 79144518Sdavidxu pcond->c_pshared = (*cond_attr)->c_pshared; 80144518Sdavidxu pcond->c_clockid = (*cond_attr)->c_clockid; 81144518Sdavidxu } 82144518Sdavidxu *cond = pcond; 83144518Sdavidxu } 84144518Sdavidxu /* Return the completion status: */ 85144518Sdavidxu return (rval); 86144518Sdavidxu} 87112918Sjeff 88144518Sdavidxustatic int 89144518Sdavidxuinit_static(struct pthread *thread, pthread_cond_t *cond) 90112918Sjeff{ 91144518Sdavidxu int ret; 92112918Sjeff 93144518Sdavidxu THR_LOCK_ACQUIRE(thread, &_cond_static_lock); 94144518Sdavidxu 95112918Sjeff if (*cond == NULL) 96144518Sdavidxu ret = cond_init(cond, NULL); 97144518Sdavidxu else 98144518Sdavidxu ret = 0; 99112918Sjeff 100144518Sdavidxu THR_LOCK_RELEASE(thread, &_cond_static_lock); 101112918Sjeff 102144518Sdavidxu return (ret); 103112918Sjeff} 104112918Sjeff 105112918Sjeffint 106112918Sjeff_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 107112918Sjeff{ 108112918Sjeff 109144518Sdavidxu *cond = NULL; 110144518Sdavidxu return (cond_init(cond, cond_attr)); 111112918Sjeff} 112112918Sjeff 113112918Sjeffint 114112918Sjeff_pthread_cond_destroy(pthread_cond_t *cond) 115112918Sjeff{ 116144518Sdavidxu struct pthread_cond *cv; 117144518Sdavidxu struct pthread *curthread = _get_curthread(); 118144518Sdavidxu int rval = 0; 119112918Sjeff 120144518Sdavidxu if (*cond == NULL) 121144518Sdavidxu rval = EINVAL; 122144518Sdavidxu else { 123144518Sdavidxu /* Lock the condition variable structure: */ 124144518Sdavidxu THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 125144518Sdavidxu if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) { 126144518Sdavidxu THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 127144518Sdavidxu return (EBUSY); 128144518Sdavidxu } 129112918Sjeff 130144518Sdavidxu /* 131144518Sdavidxu * NULL the caller's pointer now that the condition 132144518Sdavidxu * variable has been destroyed: 133144518Sdavidxu */ 134144518Sdavidxu cv = *cond; 135144518Sdavidxu *cond = NULL; 136112918Sjeff 137144518Sdavidxu /* Unlock the condition variable structure: */ 138144518Sdavidxu THR_LOCK_RELEASE(curthread, &cv->c_lock); 139112918Sjeff 140144518Sdavidxu /* Free the cond lock structure: */ 141112918Sjeff 142144518Sdavidxu /* 143144518Sdavidxu * Free the memory allocated for the condition 144144518Sdavidxu * variable structure: 145144518Sdavidxu */ 146144518Sdavidxu free(cv); 147112918Sjeff 148144518Sdavidxu } 149144518Sdavidxu /* Return the completion status: */ 150112918Sjeff return (rval); 151112918Sjeff} 152112918Sjeff 153144518Sdavidxustruct cond_cancel_info 154112918Sjeff{ 155144518Sdavidxu pthread_mutex_t *mutex; 156144518Sdavidxu pthread_cond_t *cond; 157144518Sdavidxu long seqno; 158144518Sdavidxu}; 159115035Smtm 160144518Sdavidxustatic void 161144518Sdavidxucond_cancel_handler(void *arg) 162144518Sdavidxu{ 163144518Sdavidxu struct pthread *curthread = _get_curthread(); 164144518Sdavidxu struct cond_cancel_info *cci = (struct cond_cancel_info *)arg; 165144518Sdavidxu pthread_cond_t cv; 166144518Sdavidxu 167144518Sdavidxu cv = *(cci->cond); 168144518Sdavidxu THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 169144518Sdavidxu if (cv->c_seqno != cci->seqno && cv->c_wakeups != 0) { 170144518Sdavidxu if (cv->c_waiters > 0) { 171144518Sdavidxu cv->c_seqno++; 172144518Sdavidxu _thr_umtx_wake(&cv->c_seqno, 1); 173144518Sdavidxu } else 174144518Sdavidxu cv->c_wakeups--; 175144518Sdavidxu } else { 176144518Sdavidxu cv->c_waiters--; 177144518Sdavidxu } 178144518Sdavidxu THR_LOCK_RELEASE(curthread, &cv->c_lock); 179144518Sdavidxu 180144518Sdavidxu _mutex_cv_lock(cci->mutex); 181115035Smtm} 182115035Smtm 183115035Smtmstatic int 184144518Sdavidxucond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 185144518Sdavidxu const struct timespec *abstime, int cancel) 186115035Smtm{ 187144518Sdavidxu struct pthread *curthread = _get_curthread(); 188144518Sdavidxu struct timespec ts, ts2, *tsp; 189144518Sdavidxu struct cond_cancel_info cci; 190144518Sdavidxu pthread_cond_t cv; 191144518Sdavidxu long seq, oldseq; 192144518Sdavidxu int oldcancel; 193144518Sdavidxu int ret = 0; 194112918Sjeff 195112918Sjeff /* 196144518Sdavidxu * If the condition variable is statically initialized, 197144518Sdavidxu * perform the dynamic initialization: 198112918Sjeff */ 199144518Sdavidxu if (__predict_false(*cond == NULL && 200144518Sdavidxu (ret = init_static(curthread, cond)) != 0)) 201144518Sdavidxu return (ret); 202112918Sjeff 203144518Sdavidxu cv = *cond; 204144518Sdavidxu THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 205144518Sdavidxu ret = _mutex_cv_unlock(mutex); 206144518Sdavidxu if (ret) { 207144518Sdavidxu THR_LOCK_RELEASE(curthread, &cv->c_lock); 208144518Sdavidxu return (ret); 209112918Sjeff } 210144518Sdavidxu oldseq = seq = cv->c_seqno; 211144518Sdavidxu cci.mutex = mutex; 212144518Sdavidxu cci.cond = cond; 213144518Sdavidxu cci.seqno = oldseq; 214112918Sjeff 215144518Sdavidxu cv->c_waiters++; 216144518Sdavidxu do { 217144518Sdavidxu THR_LOCK_RELEASE(curthread, &cv->c_lock); 218112918Sjeff 219144518Sdavidxu if (abstime != NULL) { 220144518Sdavidxu clock_gettime(cv->c_clockid, &ts); 221144518Sdavidxu TIMESPEC_SUB(&ts2, abstime, &ts); 222144518Sdavidxu tsp = &ts2; 223144518Sdavidxu } else 224144518Sdavidxu tsp = NULL; 225112918Sjeff 226144518Sdavidxu if (cancel) { 227144518Sdavidxu THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &cci); 228144518Sdavidxu oldcancel = _thr_cancel_enter(curthread); 229144518Sdavidxu ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp); 230144518Sdavidxu _thr_cancel_leave(curthread, oldcancel); 231144518Sdavidxu THR_CLEANUP_POP(curthread, 0); 232144518Sdavidxu } else { 233144518Sdavidxu ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp); 234129484Smtm } 235112918Sjeff 236144518Sdavidxu THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 237144518Sdavidxu seq = cv->c_seqno; 238144518Sdavidxu if (abstime != NULL && ret == ETIMEDOUT) 239144518Sdavidxu break; 240112918Sjeff 241144518Sdavidxu /* 242144518Sdavidxu * loop if we have never been told to wake up 243144518Sdavidxu * or we lost a race. 244144518Sdavidxu */ 245144518Sdavidxu } while (seq == oldseq || cv->c_wakeups == 0); 246144518Sdavidxu 247144518Sdavidxu if (seq != oldseq && cv->c_wakeups != 0) { 248144518Sdavidxu cv->c_wakeups--; 249144518Sdavidxu ret = 0; 250144518Sdavidxu } else { 251144518Sdavidxu cv->c_waiters--; 252112918Sjeff } 253144518Sdavidxu THR_LOCK_RELEASE(curthread, &cv->c_lock); 254144518Sdavidxu _mutex_cv_lock(mutex); 255144518Sdavidxu return (ret); 256112918Sjeff} 257112918Sjeff 258112918Sjeffint 259144518Sdavidxu_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 260112918Sjeff{ 261144518Sdavidxu 262144518Sdavidxu return (cond_wait_common(cond, mutex, NULL, 0)); 263112918Sjeff} 264112918Sjeff 265112918Sjeffint 266144518Sdavidxu__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 267112918Sjeff{ 268144518Sdavidxu 269144518Sdavidxu return (cond_wait_common(cond, mutex, NULL, 1)); 270115277Smtm} 271115277Smtm 272144518Sdavidxuint 273144518Sdavidxu_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 274144518Sdavidxu const struct timespec * abstime) 275115277Smtm{ 276112918Sjeff 277144518Sdavidxu if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 278144518Sdavidxu abstime->tv_nsec >= 1000000000) 279112918Sjeff return (EINVAL); 280112918Sjeff 281144518Sdavidxu return (cond_wait_common(cond, mutex, abstime, 0)); 282112918Sjeff} 283112918Sjeff 284144518Sdavidxuint 285144518Sdavidxu__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 286144518Sdavidxu const struct timespec *abstime) 287112918Sjeff{ 288112918Sjeff 289144518Sdavidxu if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 290144518Sdavidxu abstime->tv_nsec >= 1000000000) 291144518Sdavidxu return (EINVAL); 292112918Sjeff 293144518Sdavidxu return (cond_wait_common(cond, mutex, abstime, 1)); 294112918Sjeff} 295112918Sjeff 296144518Sdavidxustatic int 297144518Sdavidxucond_signal_common(pthread_cond_t *cond, int broadcast) 298112918Sjeff{ 299144518Sdavidxu struct pthread *curthread = _get_curthread(); 300144518Sdavidxu pthread_cond_t cv; 301144518Sdavidxu int ret = 0, oldwaiters; 302112918Sjeff 303112918Sjeff /* 304144518Sdavidxu * If the condition variable is statically initialized, perform dynamic 305144518Sdavidxu * initialization. 306112918Sjeff */ 307144518Sdavidxu if (__predict_false(*cond == NULL && 308144518Sdavidxu (ret = init_static(curthread, cond)) != 0)) 309144518Sdavidxu return (ret); 310144518Sdavidxu 311144518Sdavidxu cv = *cond; 312144518Sdavidxu /* Lock the condition variable structure. */ 313144518Sdavidxu THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 314144518Sdavidxu if (cv->c_waiters) { 315144518Sdavidxu if (!broadcast) { 316144518Sdavidxu cv->c_wakeups++; 317144518Sdavidxu cv->c_waiters--; 318144518Sdavidxu cv->c_seqno++; 319144518Sdavidxu _thr_umtx_wake(&cv->c_seqno, 1); 320144518Sdavidxu } else { 321144518Sdavidxu oldwaiters = cv->c_waiters; 322144518Sdavidxu cv->c_wakeups += cv->c_waiters; 323144518Sdavidxu cv->c_waiters = 0; 324144518Sdavidxu cv->c_seqno++; 325144518Sdavidxu _thr_umtx_wake(&cv->c_seqno, oldwaiters); 326144518Sdavidxu } 327112918Sjeff } 328144518Sdavidxu THR_LOCK_RELEASE(curthread, &cv->c_lock); 329144518Sdavidxu return (ret); 330112918Sjeff} 331112918Sjeff 332144518Sdavidxuint 333144518Sdavidxu_pthread_cond_signal(pthread_cond_t * cond) 334112918Sjeff{ 335112918Sjeff 336144518Sdavidxu return (cond_signal_common(cond, 0)); 337112918Sjeff} 338115389Smtm 339144518Sdavidxuint 340144518Sdavidxu_pthread_cond_broadcast(pthread_cond_t * cond) 341115389Smtm{ 342144518Sdavidxu 343144518Sdavidxu return (cond_signal_common(cond, 1)); 344115389Smtm} 345