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 { 117 /* Lock the condition variable structure: */ 118 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 119 if ((*cond)->c_waiters != 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_waiters == 0) 165 cv->c_broadcast = 0; 166 if (cv->c_seqno != info->seqno) { 167 _thr_umtx_wake(&cv->c_seqno, 1); 168 /* cv->c_seqno++; XXX why was this here? */ 169 _thr_umtx_wake(&cv->c_seqno, 1); 170 } 171 THR_LOCK_RELEASE(curthread, &cv->c_lock); 172 173 _mutex_cv_lock(info->mutex, info->count); 174 } 175 176 static int 177 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 178 const struct timespec *abstime, int cancel) 179 { 180 struct pthread *curthread = tls_get_curthread(); 181 struct timespec ts, ts2, *tsp; 182 struct cond_cancel_info info; 183 pthread_cond_t cv; 184 int seq, oldseq; 185 int oldcancel; 186 int ret = 0; 187 188 /* 189 * If the condition variable is statically initialized, 190 * perform the dynamic initialization: 191 */ 192 if (__predict_false(*cond == NULL && 193 (ret = init_static(curthread, cond)) != 0)) 194 return (ret); 195 196 cv = *cond; 197 /* fprintf(stderr, "waiton1 %p\n", cv);*/ 198 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 199 oldseq = cv->c_seqno; 200 /* fprintf(stderr, "waiton2 %p %d\n", cv, oldseq);*/ 201 ret = _mutex_cv_unlock(mutex, &info.count); 202 if (ret) { 203 THR_LOCK_RELEASE(curthread, &cv->c_lock); 204 return (ret); 205 } 206 seq = cv->c_seqno; 207 info.mutex = mutex; 208 info.cond = cond; 209 info.seqno = oldseq; 210 211 ++cv->c_waiters; 212 213 /* 214 * loop if we have never been told to wake up 215 * or we lost a race. 216 */ 217 while (seq == oldseq /* || cv->c_wakeups == 0*/) { 218 THR_LOCK_RELEASE(curthread, &cv->c_lock); 219 220 if (abstime != NULL) { 221 clock_gettime(cv->c_clockid, &ts); 222 TIMESPEC_SUB(&ts2, abstime, &ts); 223 tsp = &ts2; 224 } else 225 tsp = NULL; 226 227 if (cancel) { 228 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); 229 oldcancel = _thr_cancel_enter(curthread); 230 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp, 231 cv->c_clockid); 232 _thr_cancel_leave(curthread, oldcancel); 233 THR_CLEANUP_POP(curthread, 0); 234 } else { 235 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp, 236 cv->c_clockid); 237 } 238 239 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 240 seq = cv->c_seqno; 241 if (abstime != NULL && ret == ETIMEDOUT) 242 break; 243 } 244 if (--cv->c_waiters == 0) 245 cv->c_broadcast = 0; 246 if (seq != oldseq) 247 ret = 0; 248 THR_LOCK_RELEASE(curthread, &cv->c_lock); 249 _mutex_cv_lock(mutex, info.count); 250 return (ret); 251 } 252 253 int 254 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 255 { 256 257 return (cond_wait_common(cond, mutex, NULL, 0)); 258 } 259 260 int 261 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 262 { 263 264 return (cond_wait_common(cond, mutex, NULL, 1)); 265 } 266 267 int 268 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 269 const struct timespec * abstime) 270 { 271 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 272 abstime->tv_nsec >= 1000000000) 273 return (EINVAL); 274 275 return (cond_wait_common(cond, mutex, abstime, 0)); 276 } 277 278 int 279 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 280 const struct timespec *abstime) 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, 1)); 287 } 288 289 static int 290 cond_signal_common(pthread_cond_t *cond, int broadcast) 291 { 292 struct pthread *curthread = tls_get_curthread(); 293 pthread_cond_t cv; 294 int ret = 0; 295 296 /* 297 * If the condition variable is statically initialized, perform dynamic 298 * initialization. 299 */ 300 if (__predict_false(*cond == NULL && 301 (ret = init_static(curthread, cond)) != 0)) 302 return (ret); 303 304 cv = *cond; 305 /* fprintf(stderr, "signal %p\n", cv);*/ 306 /* Lock the condition variable structure. */ 307 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 308 cv->c_seqno++; 309 if (cv->c_broadcast == 0) 310 cv->c_broadcast = broadcast; 311 312 if (cv->c_waiters) { 313 if (cv->c_broadcast) 314 _thr_umtx_wake(&cv->c_seqno, INT_MAX); 315 else 316 _thr_umtx_wake(&cv->c_seqno, 1); 317 } 318 THR_LOCK_RELEASE(curthread, &cv->c_lock); 319 return (ret); 320 } 321 322 int 323 _pthread_cond_signal(pthread_cond_t * cond) 324 { 325 326 return (cond_signal_common(cond, 0)); 327 } 328 329 int 330 _pthread_cond_broadcast(pthread_cond_t * cond) 331 { 332 333 return (cond_signal_common(cond, 1)); 334 } 335 336 /* 337 * Double underscore versions are cancellation points. Single underscore 338 * versions are not and are provided for libc internal usage (which 339 * shouldn't introduce cancellation points). 340 */ 341 __strong_reference(__pthread_cond_wait, pthread_cond_wait); 342 __strong_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 343 344 __strong_reference(_pthread_cond_init, pthread_cond_init); 345 __strong_reference(_pthread_cond_destroy, pthread_cond_destroy); 346 __strong_reference(_pthread_cond_signal, pthread_cond_signal); 347 __strong_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 348 349