1 /* 2 * Copyright (c) 2005 David Xu <davidxu@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 * $DragonFly: src/lib/libthread_xu/thread/thr_cond.c,v 1.10 2006/04/06 23:50:13 davidxu Exp $ 27 */ 28 29 #include "namespace.h" 30 #include <machine/tls.h> 31 32 #include <stdlib.h> 33 #include <string.h> 34 #include <pthread.h> 35 #include <limits.h> 36 #include "un-namespace.h" 37 38 #include "thr_private.h" 39 40 umtx_t _cond_static_lock; 41 42 int __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 43 int __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 44 const struct timespec *abstime); 45 /* 46 * Prototypes 47 */ 48 static int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); 49 static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 50 const struct timespec *abstime, int cancel); 51 static int cond_signal_common(pthread_cond_t *cond, int broadcast); 52 53 static int 54 cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 55 { 56 pthread_cond_t pcond; 57 int rval = 0; 58 59 if ((pcond = (pthread_cond_t) 60 malloc(sizeof(struct pthread_cond))) == NULL) { 61 rval = ENOMEM; 62 } else { 63 /* 64 * Initialise the condition variable structure: 65 */ 66 _thr_umtx_init(&pcond->c_lock); 67 pcond->c_seqno = 0; 68 pcond->c_waiters = 0; 69 pcond->c_wakeups = 0; 70 if (cond_attr == NULL || *cond_attr == NULL) { 71 pcond->c_pshared = 0; 72 pcond->c_clockid = CLOCK_REALTIME; 73 } else { 74 pcond->c_pshared = (*cond_attr)->c_pshared; 75 pcond->c_clockid = (*cond_attr)->c_clockid; 76 } 77 *cond = pcond; 78 } 79 /* Return the completion status: */ 80 return (rval); 81 } 82 83 static int 84 init_static(struct pthread *thread, pthread_cond_t *cond) 85 { 86 int ret; 87 88 THR_LOCK_ACQUIRE(thread, &_cond_static_lock); 89 90 if (*cond == NULL) 91 ret = cond_init(cond, NULL); 92 else 93 ret = 0; 94 95 THR_LOCK_RELEASE(thread, &_cond_static_lock); 96 97 return (ret); 98 } 99 100 int 101 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 102 { 103 *cond = NULL; 104 return cond_init(cond, cond_attr); 105 } 106 107 int 108 _pthread_cond_destroy(pthread_cond_t *cond) 109 { 110 struct pthread_cond *cv; 111 struct pthread *curthread = tls_get_curthread(); 112 int rval = 0; 113 114 if (*cond == NULL) 115 rval = EINVAL; 116 else { 117 /* Lock the condition variable structure: */ 118 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 119 if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) { 120 THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 121 return (EBUSY); 122 } 123 124 /* 125 * NULL the caller's pointer now that the condition 126 * variable has been destroyed: 127 */ 128 cv = *cond; 129 *cond = NULL; 130 131 /* Unlock the condition variable structure: */ 132 THR_LOCK_RELEASE(curthread, &cv->c_lock); 133 134 /* Free the cond lock structure: */ 135 136 /* 137 * Free the memory allocated for the condition 138 * variable structure: 139 */ 140 free(cv); 141 142 } 143 /* Return the completion status: */ 144 return (rval); 145 } 146 147 struct cond_cancel_info 148 { 149 pthread_mutex_t *mutex; 150 pthread_cond_t *cond; 151 int seqno; 152 int count; 153 }; 154 155 static void 156 cond_cancel_handler(void *arg) 157 { 158 struct pthread *curthread = tls_get_curthread(); 159 struct cond_cancel_info *info = (struct cond_cancel_info *)arg; 160 pthread_cond_t cv; 161 162 cv = *(info->cond); 163 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 164 if (cv->c_seqno != info->seqno && cv->c_wakeups != 0) { 165 if (cv->c_waiters > 0) { 166 cv->c_seqno++; 167 _thr_umtx_wake(&cv->c_seqno, 1); 168 } else 169 cv->c_wakeups--; 170 } else { 171 cv->c_waiters--; 172 } 173 THR_LOCK_RELEASE(curthread, &cv->c_lock); 174 175 _mutex_cv_lock(info->mutex, info->count); 176 } 177 178 static int 179 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 180 const struct timespec *abstime, int cancel) 181 { 182 struct pthread *curthread = tls_get_curthread(); 183 struct timespec ts, ts2, *tsp; 184 struct cond_cancel_info info; 185 pthread_cond_t cv; 186 int seq, oldseq; 187 int oldcancel; 188 int ret = 0; 189 190 /* 191 * If the condition variable is statically initialized, 192 * perform the dynamic initialization: 193 */ 194 if (__predict_false(*cond == NULL && 195 (ret = init_static(curthread, cond)) != 0)) 196 return (ret); 197 198 cv = *cond; 199 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 200 ret = _mutex_cv_unlock(mutex, &info.count); 201 if (ret) { 202 THR_LOCK_RELEASE(curthread, &cv->c_lock); 203 return (ret); 204 } 205 oldseq = seq = cv->c_seqno; 206 info.mutex = mutex; 207 info.cond = cond; 208 info.seqno = oldseq; 209 210 cv->c_waiters++; 211 do { 212 THR_LOCK_RELEASE(curthread, &cv->c_lock); 213 214 if (abstime != NULL) { 215 clock_gettime(cv->c_clockid, &ts); 216 TIMESPEC_SUB(&ts2, abstime, &ts); 217 tsp = &ts2; 218 } else 219 tsp = NULL; 220 221 if (cancel) { 222 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); 223 oldcancel = _thr_cancel_enter(curthread); 224 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp, 225 cv->c_clockid); 226 _thr_cancel_leave(curthread, oldcancel); 227 THR_CLEANUP_POP(curthread, 0); 228 } else { 229 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp, 230 cv->c_clockid); 231 } 232 233 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 234 seq = cv->c_seqno; 235 if (abstime != NULL && ret == ETIMEDOUT) 236 break; 237 238 /* 239 * loop if we have never been told to wake up 240 * or we lost a race. 241 */ 242 } while (seq == oldseq || cv->c_wakeups == 0); 243 244 if (seq != oldseq && cv->c_wakeups != 0) { 245 cv->c_wakeups--; 246 ret = 0; 247 } else { 248 cv->c_waiters--; 249 } 250 THR_LOCK_RELEASE(curthread, &cv->c_lock); 251 _mutex_cv_lock(mutex, info.count); 252 return (ret); 253 } 254 255 int 256 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 257 { 258 259 return (cond_wait_common(cond, mutex, NULL, 0)); 260 } 261 262 int 263 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 264 { 265 266 return (cond_wait_common(cond, mutex, NULL, 1)); 267 } 268 269 int 270 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 271 const struct timespec * abstime) 272 { 273 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 274 abstime->tv_nsec >= 1000000000) 275 return (EINVAL); 276 277 return (cond_wait_common(cond, mutex, abstime, 0)); 278 } 279 280 int 281 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 282 const struct timespec *abstime) 283 { 284 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 285 abstime->tv_nsec >= 1000000000) 286 return (EINVAL); 287 288 return (cond_wait_common(cond, mutex, abstime, 1)); 289 } 290 291 static int 292 cond_signal_common(pthread_cond_t *cond, int broadcast) 293 { 294 struct pthread *curthread = tls_get_curthread(); 295 pthread_cond_t cv; 296 int ret = 0; 297 298 /* 299 * If the condition variable is statically initialized, perform dynamic 300 * initialization. 301 */ 302 if (__predict_false(*cond == NULL && 303 (ret = init_static(curthread, cond)) != 0)) 304 return (ret); 305 306 cv = *cond; 307 /* Lock the condition variable structure. */ 308 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 309 if (cv->c_waiters) { 310 if (!broadcast) { 311 cv->c_wakeups++; 312 cv->c_waiters--; 313 cv->c_seqno++; 314 _thr_umtx_wake(&cv->c_seqno, 1); 315 } else { 316 cv->c_wakeups += cv->c_waiters; 317 cv->c_waiters = 0; 318 cv->c_seqno++; 319 _thr_umtx_wake(&cv->c_seqno, INT_MAX); 320 } 321 } 322 THR_LOCK_RELEASE(curthread, &cv->c_lock); 323 return (ret); 324 } 325 326 int 327 _pthread_cond_signal(pthread_cond_t * cond) 328 { 329 330 return (cond_signal_common(cond, 0)); 331 } 332 333 int 334 _pthread_cond_broadcast(pthread_cond_t * cond) 335 { 336 337 return (cond_signal_common(cond, 1)); 338 } 339 340 /* 341 * Double underscore versions are cancellation points. Single underscore 342 * versions are not and are provided for libc internal usage (which 343 * shouldn't introduce cancellation points). 344 */ 345 __strong_reference(__pthread_cond_wait, pthread_cond_wait); 346 __strong_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 347 348 __strong_reference(_pthread_cond_init, pthread_cond_init); 349 __strong_reference(_pthread_cond_destroy, pthread_cond_destroy); 350 __strong_reference(_pthread_cond_signal, pthread_cond_signal); 351 __strong_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 352 353