thr_cond.c revision 164583
1311116Sdim/* 2311116Sdim * Copyright (c) 2005 David Xu <davidxu@freebsd.org> 3353358Sdim * All rights reserved. 4353358Sdim * 5353358Sdim * Redistribution and use in source and binary forms, with or without 6311116Sdim * modification, are permitted provided that the following conditions 7311116Sdim * are met: 8311116Sdim * 1. Redistributions of source code must retain the above copyright 9311116Sdim * notice unmodified, this list of conditions, and the following 10311116Sdim * disclaimer. 11311116Sdim * 2. Redistributions in binary form must reproduce the above copyright 12311116Sdim * notice, this list of conditions and the following disclaimer in the 13311116Sdim * documentation and/or other materials provided with the distribution. 14311116Sdim * 15311116Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16311116Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17311116Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18311116Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19311116Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20311116Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21311116Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22311116Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23311116Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24311116Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25311116Sdim * 26311116Sdim * $FreeBSD: head/lib/libthr/thread/thr_cond.c 164583 2006-11-24 09:57:38Z davidxu $ 27311116Sdim */ 28311116Sdim 29311116Sdim#include "namespace.h" 30311116Sdim#include <stdlib.h> 31311116Sdim#include <errno.h> 32311116Sdim#include <string.h> 33311116Sdim#include <pthread.h> 34311116Sdim#include <limits.h> 35311116Sdim#include "un-namespace.h" 36311116Sdim 37311116Sdim#include "thr_private.h" 38311116Sdim 39311116Sdim/* 40311116Sdim * Prototypes 41311116Sdim */ 42311116Sdimint __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 43311116Sdimint __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 44311116Sdim const struct timespec * abstime); 45311116Sdimstatic int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); 46311116Sdimstatic int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 47311116Sdim const struct timespec *abstime, int cancel); 48311116Sdimstatic int cond_signal_common(pthread_cond_t *cond, int broadcast); 49311116Sdim 50311116Sdim/* 51311116Sdim * Double underscore versions are cancellation points. Single underscore 52311116Sdim * versions are not and are provided for libc internal usage (which 53311116Sdim * shouldn't introduce cancellation points). 54311116Sdim */ 55311116Sdim__weak_reference(__pthread_cond_wait, pthread_cond_wait); 56311116Sdim__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 57311116Sdim 58311116Sdim__weak_reference(_pthread_cond_init, pthread_cond_init); 59311116Sdim__weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 60311116Sdim__weak_reference(_pthread_cond_signal, pthread_cond_signal); 61311116Sdim__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 62311116Sdim 63311116Sdimstatic int 64311116Sdimcond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 65311116Sdim{ 66311116Sdim pthread_cond_t pcond; 67311116Sdim int rval = 0; 68311116Sdim 69311116Sdim if ((pcond = (pthread_cond_t) 70 malloc(sizeof(struct pthread_cond))) == NULL) { 71 rval = ENOMEM; 72 } else { 73 /* 74 * Initialise the condition variable structure: 75 */ 76 _thr_umutex_init(&pcond->c_lock); 77 pcond->c_seqno = 0; 78 pcond->c_waiters = 0; 79 pcond->c_wakeups = 0; 80 if (cond_attr == NULL || *cond_attr == NULL) { 81 pcond->c_pshared = 0; 82 pcond->c_clockid = CLOCK_REALTIME; 83 } else { 84 pcond->c_pshared = (*cond_attr)->c_pshared; 85 pcond->c_clockid = (*cond_attr)->c_clockid; 86 } 87 *cond = pcond; 88 } 89 /* Return the completion status: */ 90 return (rval); 91} 92 93static int 94init_static(struct pthread *thread, pthread_cond_t *cond) 95{ 96 int ret; 97 98 THR_LOCK_ACQUIRE(thread, &_cond_static_lock); 99 100 if (*cond == NULL) 101 ret = cond_init(cond, NULL); 102 else 103 ret = 0; 104 105 THR_LOCK_RELEASE(thread, &_cond_static_lock); 106 107 return (ret); 108} 109 110int 111_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 112{ 113 114 *cond = NULL; 115 return (cond_init(cond, cond_attr)); 116} 117 118int 119_pthread_cond_destroy(pthread_cond_t *cond) 120{ 121 struct pthread_cond *cv; 122 struct pthread *curthread = _get_curthread(); 123 int rval = 0; 124 125 if (*cond == NULL) 126 rval = EINVAL; 127 else { 128 /* Lock the condition variable structure: */ 129 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 130 if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) { 131 THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 132 return (EBUSY); 133 } 134 135 /* 136 * NULL the caller's pointer now that the condition 137 * variable has been destroyed: 138 */ 139 cv = *cond; 140 *cond = NULL; 141 142 /* Unlock the condition variable structure: */ 143 THR_LOCK_RELEASE(curthread, &cv->c_lock); 144 145 /* Free the cond lock structure: */ 146 147 /* 148 * Free the memory allocated for the condition 149 * variable structure: 150 */ 151 free(cv); 152 153 } 154 /* Return the completion status: */ 155 return (rval); 156} 157 158struct cond_cancel_info 159{ 160 pthread_mutex_t *mutex; 161 pthread_cond_t *cond; 162 long seqno; 163 int count; 164}; 165 166static void 167cond_cancel_handler(void *arg) 168{ 169 struct pthread *curthread = _get_curthread(); 170 struct cond_cancel_info *info = (struct cond_cancel_info *)arg; 171 pthread_cond_t cv; 172 173 cv = *(info->cond); 174 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 175 if (cv->c_seqno != info->seqno && cv->c_wakeups != 0) { 176 if (cv->c_waiters > 0) { 177 cv->c_seqno++; 178 _thr_umtx_wake(&cv->c_seqno, 1); 179 } else 180 cv->c_wakeups--; 181 } else { 182 cv->c_waiters--; 183 } 184 THR_LOCK_RELEASE(curthread, &cv->c_lock); 185 186 _mutex_cv_lock(info->mutex, info->count); 187} 188 189static int 190cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 191 const struct timespec *abstime, int cancel) 192{ 193 struct pthread *curthread = _get_curthread(); 194 struct timespec ts, ts2, *tsp; 195 struct cond_cancel_info info; 196 pthread_cond_t cv; 197 long seq, oldseq; 198 int ret = 0; 199 200 /* 201 * If the condition variable is statically initialized, 202 * perform the dynamic initialization: 203 */ 204 if (__predict_false(*cond == NULL && 205 (ret = init_static(curthread, cond)) != 0)) 206 return (ret); 207 208 cv = *cond; 209 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 210 ret = _mutex_cv_unlock(mutex, &info.count); 211 if (ret) { 212 THR_LOCK_RELEASE(curthread, &cv->c_lock); 213 return (ret); 214 } 215 oldseq = seq = cv->c_seqno; 216 info.mutex = mutex; 217 info.cond = cond; 218 info.seqno = oldseq; 219 220 cv->c_waiters++; 221 do { 222 THR_LOCK_RELEASE(curthread, &cv->c_lock); 223 224 if (abstime != NULL) { 225 clock_gettime(cv->c_clockid, &ts); 226 TIMESPEC_SUB(&ts2, abstime, &ts); 227 tsp = &ts2; 228 } else 229 tsp = NULL; 230 231 if (cancel) { 232 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); 233 _thr_cancel_enter(curthread); 234 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp); 235 _thr_cancel_leave(curthread); 236 THR_CLEANUP_POP(curthread, 0); 237 } else { 238 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp); 239 } 240 241 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 242 seq = cv->c_seqno; 243 if (abstime != NULL && ret == ETIMEDOUT) 244 break; 245 246 /* 247 * loop if we have never been told to wake up 248 * or we lost a race. 249 */ 250 } while (seq == oldseq || cv->c_wakeups == 0); 251 252 if (seq != oldseq && cv->c_wakeups != 0) { 253 cv->c_wakeups--; 254 ret = 0; 255 } else { 256 cv->c_waiters--; 257 } 258 THR_LOCK_RELEASE(curthread, &cv->c_lock); 259 _mutex_cv_lock(mutex, info.count); 260 return (ret); 261} 262 263int 264_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 265{ 266 267 return (cond_wait_common(cond, mutex, NULL, 0)); 268} 269 270int 271__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 272{ 273 274 return (cond_wait_common(cond, mutex, NULL, 1)); 275} 276 277int 278_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 279 const struct timespec * abstime) 280{ 281 282 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 283 abstime->tv_nsec >= 1000000000) 284 return (EINVAL); 285 286 return (cond_wait_common(cond, mutex, abstime, 0)); 287} 288 289int 290__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 291 const struct timespec *abstime) 292{ 293 294 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 295 abstime->tv_nsec >= 1000000000) 296 return (EINVAL); 297 298 return (cond_wait_common(cond, mutex, abstime, 1)); 299} 300 301static int 302cond_signal_common(pthread_cond_t *cond, int broadcast) 303{ 304 struct pthread *curthread = _get_curthread(); 305 pthread_cond_t cv; 306 int ret = 0, oldwaiters; 307 308 /* 309 * If the condition variable is statically initialized, perform dynamic 310 * initialization. 311 */ 312 if (__predict_false(*cond == NULL && 313 (ret = init_static(curthread, cond)) != 0)) 314 return (ret); 315 316 cv = *cond; 317 /* Lock the condition variable structure. */ 318 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 319 if (cv->c_waiters) { 320 if (!broadcast) { 321 cv->c_wakeups++; 322 cv->c_waiters--; 323 cv->c_seqno++; 324 _thr_umtx_wake(&cv->c_seqno, 1); 325 } else { 326 oldwaiters = cv->c_waiters; 327 cv->c_wakeups += cv->c_waiters; 328 cv->c_waiters = 0; 329 cv->c_seqno++; 330 _thr_umtx_wake(&cv->c_seqno, oldwaiters); 331 } 332 } 333 THR_LOCK_RELEASE(curthread, &cv->c_lock); 334 return (ret); 335} 336 337int 338_pthread_cond_signal(pthread_cond_t * cond) 339{ 340 341 return (cond_signal_common(cond, 0)); 342} 343 344int 345_pthread_cond_broadcast(pthread_cond_t * cond) 346{ 347 348 return (cond_signal_common(cond, 1)); 349} 350