thr_cond.c revision 162061
1228753Smm/* 2228753Smm * Copyright (c) 2005 David Xu <davidxu@freebsd.org> 3248616Smm * All rights reserved. 4228753Smm * 5228753Smm * Redistribution and use in source and binary forms, with or without 6228753Smm * modification, are permitted provided that the following conditions 7228753Smm * are met: 8228753Smm * 1. Redistributions of source code must retain the above copyright 9228753Smm * notice unmodified, this list of conditions, and the following 10228753Smm * disclaimer. 11228753Smm * 2. Redistributions in binary form must reproduce the above copyright 12228753Smm * notice, this list of conditions and the following disclaimer in the 13228753Smm * documentation and/or other materials provided with the distribution. 14228753Smm * 15228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18228753Smm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25228753Smm * 26228753Smm * $FreeBSD: head/lib/libthr/thread/thr_cond.c 162061 2006-09-06 04:04:10Z davidxu $ 27228753Smm */ 28231200Smm 29228753Smm#include "namespace.h" 30228753Smm#include <stdlib.h> 31228753Smm#include <errno.h> 32228753Smm#include <string.h> 33228753Smm#include <pthread.h> 34228753Smm#include <limits.h> 35228753Smm#include "un-namespace.h" 36228753Smm 37228753Smm#include "thr_private.h" 38228753Smm 39228753Smm/* 40228753Smm * Prototypes 41228753Smm */ 42228753Smmint __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 43228753Smmint __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 44228753Smm const struct timespec * abstime); 45228753Smmstatic int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); 46228753Smmstatic int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 47228753Smm const struct timespec *abstime, int cancel); 48228753Smmstatic int cond_signal_common(pthread_cond_t *cond, int broadcast); 49228753Smm 50228753Smm/* 51228753Smm * Double underscore versions are cancellation points. Single underscore 52228753Smm * versions are not and are provided for libc internal usage (which 53228753Smm * shouldn't introduce cancellation points). 54228753Smm */ 55228753Smm__weak_reference(__pthread_cond_wait, pthread_cond_wait); 56228753Smm__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 57248616Smm 58228753Smm__weak_reference(_pthread_cond_init, pthread_cond_init); 59248616Smm__weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 60228753Smm__weak_reference(_pthread_cond_signal, pthread_cond_signal); 61231200Smm__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 62231200Smm 63231200Smmstatic int 64228753Smmcond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 65228753Smm{ 66228753Smm pthread_cond_t pcond; 67231200Smm int rval = 0; 68228753Smm 69228753Smm if ((pcond = (pthread_cond_t) 70231200Smm malloc(sizeof(struct pthread_cond))) == NULL) { 71231200Smm rval = ENOMEM; 72231200Smm } else { 73231200Smm /* 74231200Smm * Initialise the condition variable structure: 75231200Smm */ 76231200Smm _thr_umutex_init(&pcond->c_lock); 77231200Smm pcond->c_seqno = 0; 78228753Smm pcond->c_waiters = 0; 79231200Smm pcond->c_wakeups = 0; 80231200Smm if (cond_attr == NULL || *cond_attr == NULL) { 81231200Smm pcond->c_pshared = 0; 82231200Smm pcond->c_clockid = CLOCK_REALTIME; 83231200Smm } else { 84231200Smm pcond->c_pshared = (*cond_attr)->c_pshared; 85228753Smm pcond->c_clockid = (*cond_attr)->c_clockid; 86228753Smm } 87228753Smm *cond = pcond; 88228753Smm } 89228753Smm /* Return the completion status: */ 90228753Smm return (rval); 91248616Smm} 92228753Smm 93228753Smmstatic int 94228753Smminit_static(struct pthread *thread, pthread_cond_t *cond) 95228753Smm{ 96228753Smm int ret; 97228753Smm 98228753Smm THR_LOCK_ACQUIRE(thread, &_cond_static_lock); 99228753Smm 100228753Smm if (*cond == NULL) 101228753Smm ret = cond_init(cond, NULL); 102228753Smm else 103228753Smm ret = 0; 104228753Smm 105228753Smm THR_LOCK_RELEASE(thread, &_cond_static_lock); 106228753Smm 107248616Smm return (ret); 108248616Smm} 109248616Smm 110248616Smmint 111228753Smm_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 112248616Smm{ 113228753Smm 114228753Smm *cond = NULL; 115228753Smm return (cond_init(cond, cond_attr)); 116228753Smm} 117228753Smm 118228753Smmint 119228753Smm_pthread_cond_destroy(pthread_cond_t *cond) 120228753Smm{ 121228753Smm struct pthread_cond *cv; 122228753Smm struct pthread *curthread = _get_curthread(); 123228753Smm int rval = 0; 124248616Smm 125228753Smm if (*cond == NULL) 126248616Smm rval = EINVAL; 127248616Smm else { 128248616Smm /* Lock the condition variable structure: */ 129248616Smm THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 130248616Smm if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) { 131248616Smm THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 132248616Smm return (EBUSY); 133248616Smm } 134248616Smm 135248616Smm /* 136248616Smm * NULL the caller's pointer now that the condition 137248616Smm * variable has been destroyed: 138248616Smm */ 139248616Smm cv = *cond; 140248616Smm *cond = NULL; 141248616Smm 142248616Smm /* Unlock the condition variable structure: */ 143248616Smm THR_LOCK_RELEASE(curthread, &cv->c_lock); 144248616Smm 145248616Smm /* Free the cond lock structure: */ 146248616Smm 147248616Smm /* 148228753Smm * Free the memory allocated for the condition 149231200Smm * variable structure: 150228753Smm */ 151228753Smm free(cv); 152228753Smm 153228753Smm } 154228753Smm /* Return the completion status: */ 155228753Smm return (rval); 156228753Smm} 157228753Smm 158228753Smmstruct cond_cancel_info 159231200Smm{ 160228753Smm pthread_mutex_t *mutex; 161228753Smm pthread_cond_t *cond; 162228753Smm long seqno; 163228753Smm int count; 164228753Smm}; 165248616Smm 166228753Smmstatic void 167248616Smmcond_cancel_handler(void *arg) 168228753Smm{ 169248616Smm struct pthread *curthread = _get_curthread(); 170248616Smm struct cond_cancel_info *info = (struct cond_cancel_info *)arg; 171228753Smm pthread_cond_t cv; 172248616Smm 173248616Smm cv = *(info->cond); 174248616Smm THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 175248616Smm if (cv->c_seqno != info->seqno && cv->c_wakeups != 0) { 176248616Smm if (cv->c_waiters > 0) { 177228753Smm cv->c_seqno++; 178228753Smm _thr_umtx_wake(&cv->c_seqno, 1); 179228753Smm } else 180228753Smm cv->c_wakeups--; 181228753Smm } else { 182228753Smm cv->c_waiters--; 183248616Smm } 184248616Smm THR_LOCK_RELEASE(curthread, &cv->c_lock); 185228753Smm 186228753Smm _mutex_cv_lock(info->mutex, info->count); 187228753Smm} 188248616Smm 189248616Smmstatic int 190248616Smmcond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 191248616Smm const struct timespec *abstime, int cancel) 192248616Smm{ 193248616Smm struct pthread *curthread = _get_curthread(); 194248616Smm struct timespec ts, ts2, *tsp; 195248616Smm struct cond_cancel_info info; 196248616Smm pthread_cond_t cv; 197248616Smm long seq, oldseq; 198248616Smm int oldcancel; 199228753Smm int ret = 0; 200228753Smm 201228753Smm /* 202228753Smm * If the condition variable is statically initialized, 203228753Smm * perform the dynamic initialization: 204228753Smm */ 205228753Smm if (__predict_false(*cond == NULL && 206228753Smm (ret = init_static(curthread, cond)) != 0)) 207228753Smm return (ret); 208228753Smm 209228753Smm cv = *cond; 210228753Smm THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 211228753Smm ret = _mutex_cv_unlock(mutex, &info.count); 212228753Smm if (ret) { 213228753Smm THR_LOCK_RELEASE(curthread, &cv->c_lock); 214228753Smm return (ret); 215228753Smm } 216228753Smm oldseq = seq = cv->c_seqno; 217228753Smm info.mutex = mutex; 218228753Smm info.cond = cond; 219228753Smm info.seqno = oldseq; 220228753Smm 221228753Smm cv->c_waiters++; 222228753Smm do { 223228753Smm THR_LOCK_RELEASE(curthread, &cv->c_lock); 224228753Smm 225228753Smm if (abstime != NULL) { 226228753Smm clock_gettime(cv->c_clockid, &ts); 227228753Smm TIMESPEC_SUB(&ts2, abstime, &ts); 228228753Smm tsp = &ts2; 229228753Smm } else 230228753Smm tsp = NULL; 231228753Smm 232228753Smm if (cancel) { 233228753Smm THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); 234228753Smm oldcancel = _thr_cancel_enter(curthread); 235228753Smm ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp); 236228753Smm _thr_cancel_leave(curthread, oldcancel); 237228753Smm THR_CLEANUP_POP(curthread, 0); 238228753Smm } else { 239228753Smm ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp); 240228753Smm } 241228753Smm 242228753Smm THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 243228753Smm seq = cv->c_seqno; 244228753Smm if (abstime != NULL && ret == ETIMEDOUT) 245228753Smm break; 246228753Smm 247228753Smm /* 248228753Smm * loop if we have never been told to wake up 249228753Smm * or we lost a race. 250228753Smm */ 251228753Smm } while (seq == oldseq || cv->c_wakeups == 0); 252228753Smm 253228753Smm if (seq != oldseq && cv->c_wakeups != 0) { 254228753Smm cv->c_wakeups--; 255228753Smm ret = 0; 256228753Smm } else { 257248616Smm cv->c_waiters--; 258248616Smm } 259248616Smm THR_LOCK_RELEASE(curthread, &cv->c_lock); 260228753Smm _mutex_cv_lock(mutex, info.count); 261228753Smm return (ret); 262228753Smm} 263228753Smm 264228753Smmint 265228753Smm_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 266228753Smm{ 267228753Smm 268228753Smm return (cond_wait_common(cond, mutex, NULL, 0)); 269228753Smm} 270228753Smm 271228753Smmint 272228753Smm__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 273228753Smm{ 274228753Smm 275228753Smm return (cond_wait_common(cond, mutex, NULL, 1)); 276228753Smm} 277228753Smm 278228753Smmint 279228753Smm_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 280228753Smm const struct timespec * abstime) 281228753Smm{ 282228753Smm 283228753Smm if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 284228753Smm abstime->tv_nsec >= 1000000000) 285228753Smm return (EINVAL); 286228753Smm 287228753Smm return (cond_wait_common(cond, mutex, abstime, 0)); 288228753Smm} 289228753Smm 290228753Smmint 291228753Smm__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 292228753Smm const struct timespec *abstime) 293228753Smm{ 294228753Smm 295228753Smm if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 296228753Smm abstime->tv_nsec >= 1000000000) 297228753Smm return (EINVAL); 298228753Smm 299228753Smm return (cond_wait_common(cond, mutex, abstime, 1)); 300228753Smm} 301228753Smm 302228753Smmstatic int 303228753Smmcond_signal_common(pthread_cond_t *cond, int broadcast) 304228753Smm{ 305228753Smm struct pthread *curthread = _get_curthread(); 306228753Smm pthread_cond_t cv; 307228753Smm int ret = 0, oldwaiters; 308228753Smm 309228753Smm /* 310228753Smm * If the condition variable is statically initialized, perform dynamic 311228753Smm * initialization. 312248616Smm */ 313248616Smm if (__predict_false(*cond == NULL && 314248616Smm (ret = init_static(curthread, cond)) != 0)) 315228753Smm return (ret); 316228753Smm 317228753Smm cv = *cond; 318228753Smm /* Lock the condition variable structure. */ 319228753Smm THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 320248616Smm if (cv->c_waiters) { 321248616Smm if (!broadcast) { 322248616Smm cv->c_wakeups++; 323248616Smm cv->c_waiters--; 324248616Smm cv->c_seqno++; 325248616Smm _thr_umtx_wake(&cv->c_seqno, 1); 326248616Smm } else { 327248616Smm oldwaiters = cv->c_waiters; 328248616Smm cv->c_wakeups += cv->c_waiters; 329248616Smm cv->c_waiters = 0; 330248616Smm cv->c_seqno++; 331248616Smm _thr_umtx_wake(&cv->c_seqno, oldwaiters); 332248616Smm } 333248616Smm } 334248616Smm THR_LOCK_RELEASE(curthread, &cv->c_lock); 335248616Smm return (ret); 336248616Smm} 337248616Smm 338248616Smmint 339248616Smm_pthread_cond_signal(pthread_cond_t * cond) 340248616Smm{ 341228753Smm 342228753Smm return (cond_signal_common(cond, 0)); 343228753Smm} 344228753Smm 345228753Smmint 346228753Smm_pthread_cond_broadcast(pthread_cond_t * cond) 347228753Smm{ 348228753Smm 349228753Smm return (cond_signal_common(cond, 1)); 350228753Smm} 351228753Smm