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.12 2008/04/15 01:45:22 dillon 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_broadcast = 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 if (*cond == NULL) 117 rval = 0; 118 else { 119 /* Lock the condition variable structure: */ 120 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 121 if ((*cond)->c_waiters != 0) { 122 THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 123 return (EBUSY); 124 } 125 126 /* 127 * NULL the caller's pointer now that the condition 128 * variable has been destroyed: 129 */ 130 cv = *cond; 131 *cond = NULL; 132 133 /* Unlock the condition variable structure: */ 134 THR_LOCK_RELEASE(curthread, &cv->c_lock); 135 136 /* Free the cond lock structure: */ 137 138 /* 139 * Free the memory allocated for the condition 140 * variable structure: 141 */ 142 free(cv); 143 144 } 145 /* Return the completion status: */ 146 return (rval); 147 } 148 149 struct cond_cancel_info 150 { 151 pthread_mutex_t *mutex; 152 pthread_cond_t *cond; 153 int seqno; 154 int count; 155 }; 156 157 static void 158 cond_cancel_handler(void *arg) 159 { 160 struct pthread *curthread = tls_get_curthread(); 161 struct cond_cancel_info *info = (struct cond_cancel_info *)arg; 162 pthread_cond_t cv; 163 164 cv = *(info->cond); 165 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 166 if (--cv->c_waiters == 0) 167 cv->c_broadcast = 0; 168 if (cv->c_seqno != info->seqno) { 169 _thr_umtx_wake(&cv->c_seqno, 1); 170 /* cv->c_seqno++; XXX why was this here? */ 171 _thr_umtx_wake(&cv->c_seqno, 1); 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 /* fprintf(stderr, "waiton1 %p\n", cv);*/ 200 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 201 oldseq = cv->c_seqno; 202 /* fprintf(stderr, "waiton2 %p %d\n", cv, oldseq);*/ 203 ret = _mutex_cv_unlock(mutex, &info.count); 204 if (ret) { 205 THR_LOCK_RELEASE(curthread, &cv->c_lock); 206 return (ret); 207 } 208 seq = cv->c_seqno; 209 info.mutex = mutex; 210 info.cond = cond; 211 info.seqno = oldseq; 212 213 ++cv->c_waiters; 214 215 /* 216 * loop if we have never been told to wake up 217 * or we lost a race. 218 */ 219 while (seq == oldseq /* || cv->c_wakeups == 0*/) { 220 THR_LOCK_RELEASE(curthread, &cv->c_lock); 221 222 if (abstime != NULL) { 223 clock_gettime(cv->c_clockid, &ts); 224 TIMESPEC_SUB(&ts2, abstime, &ts); 225 tsp = &ts2; 226 } else 227 tsp = NULL; 228 229 if (cancel) { 230 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); 231 oldcancel = _thr_cancel_enter(curthread); 232 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp, 233 cv->c_clockid); 234 _thr_cancel_leave(curthread, oldcancel); 235 THR_CLEANUP_POP(curthread, 0); 236 } else { 237 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp, 238 cv->c_clockid); 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 if (--cv->c_waiters == 0) 247 cv->c_broadcast = 0; 248 if (seq != oldseq) 249 ret = 0; 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 /* fprintf(stderr, "signal %p\n", cv);*/ 308 /* Lock the condition variable structure. */ 309 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 310 cv->c_seqno++; 311 if (cv->c_broadcast == 0) 312 cv->c_broadcast = broadcast; 313 314 if (cv->c_waiters) { 315 if (cv->c_broadcast) 316 _thr_umtx_wake(&cv->c_seqno, INT_MAX); 317 else 318 _thr_umtx_wake(&cv->c_seqno, 1); 319 } 320 THR_LOCK_RELEASE(curthread, &cv->c_lock); 321 return (ret); 322 } 323 324 int 325 _pthread_cond_signal(pthread_cond_t * cond) 326 { 327 328 return (cond_signal_common(cond, 0)); 329 } 330 331 int 332 _pthread_cond_broadcast(pthread_cond_t * cond) 333 { 334 335 return (cond_signal_common(cond, 1)); 336 } 337 338 /* 339 * Double underscore versions are cancellation points. Single underscore 340 * versions are not and are provided for libc internal usage (which 341 * shouldn't introduce cancellation points). 342 */ 343 __strong_reference(__pthread_cond_wait, pthread_cond_wait); 344 __strong_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 345 346 __strong_reference(_pthread_cond_init, pthread_cond_init); 347 __strong_reference(_pthread_cond_destroy, pthread_cond_destroy); 348 __strong_reference(_pthread_cond_signal, pthread_cond_signal); 349 __strong_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 350 351