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 */ 27 28 #include "namespace.h" 29 #include <machine/tls.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <pthread.h> 33 #include <limits.h> 34 #include "un-namespace.h" 35 36 #include "thr_private.h" 37 38 #ifdef _PTHREADS_DEBUGGING 39 #include <stdio.h> 40 #include <stdarg.h> 41 #include <sys/file.h> 42 #endif 43 44 #define cpu_ccfence() __asm __volatile("" : : : "memory") 45 46 umtx_t _cond_static_lock; 47 48 #ifdef _PTHREADS_DEBUGGING 49 50 static 51 void 52 cond_log(const char *ctl, ...) 53 { 54 char buf[256]; 55 va_list va; 56 size_t len; 57 58 va_start(va, ctl); 59 len = vsnprintf(buf, sizeof(buf), ctl, va); 60 va_end(va); 61 _thr_log(buf, len); 62 } 63 64 #else 65 66 static __inline 67 void 68 cond_log(const char *ctl __unused, ...) 69 { 70 } 71 72 #endif 73 74 /* 75 * Prototypes 76 */ 77 int __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 78 int __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 79 const struct timespec *abstime); 80 static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 81 const struct timespec *abstime, int cancel); 82 static int cond_signal_common(pthread_cond_t *cond, int broadcast); 83 84 static int 85 cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 86 { 87 pthread_cond_t pcond; 88 int rval = 0; 89 90 if ((pcond = (pthread_cond_t) 91 malloc(sizeof(struct pthread_cond))) == NULL) { 92 rval = ENOMEM; 93 } else { 94 /* 95 * Initialise the condition variable structure: 96 */ 97 _thr_umtx_init(&pcond->c_lock); 98 if (cond_attr == NULL || *cond_attr == NULL) { 99 pcond->c_pshared = 0; 100 pcond->c_clockid = CLOCK_REALTIME; 101 } else { 102 pcond->c_pshared = (*cond_attr)->c_pshared; 103 pcond->c_clockid = (*cond_attr)->c_clockid; 104 } 105 TAILQ_INIT(&pcond->c_waitlist); 106 *cond = pcond; 107 } 108 /* Return the completion status: */ 109 return (rval); 110 } 111 112 #if 0 113 void 114 _cond_reinit(pthread_cond_t cond) 115 { 116 if (cond) { 117 _thr_umtx_init(&cond->c_lock); 118 #if 0 119 /* retain state */ 120 cond->c_pshared = 0; 121 cond->c_clockid = CLOCK_REALTIME; 122 #endif 123 TAILQ_INIT(&cond->c_waitlist); 124 } 125 } 126 #endif 127 128 static int 129 init_static(struct pthread *thread, pthread_cond_t *cond) 130 { 131 int ret; 132 133 THR_LOCK_ACQUIRE(thread, &_cond_static_lock); 134 135 if (*cond == NULL) 136 ret = cond_init(cond, NULL); 137 else 138 ret = 0; 139 140 THR_LOCK_RELEASE(thread, &_cond_static_lock); 141 142 return (ret); 143 } 144 145 int 146 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 147 { 148 *cond = NULL; 149 return cond_init(cond, cond_attr); 150 } 151 152 int 153 _pthread_cond_destroy(pthread_cond_t *cond) 154 { 155 struct pthread_cond *cv; 156 struct pthread *curthread = tls_get_curthread(); 157 int rval = 0; 158 159 if (cond == NULL) 160 rval = EINVAL; 161 else if (*cond == NULL) 162 rval = 0; 163 else { 164 /* Lock the condition variable structure: */ 165 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 166 if (TAILQ_FIRST(&(*cond)->c_waitlist)) { 167 THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 168 return (EBUSY); 169 } 170 171 /* 172 * NULL the caller's pointer now that the condition 173 * variable has been destroyed: 174 */ 175 cv = *cond; 176 *cond = NULL; 177 178 /* Unlock the condition variable structure: */ 179 THR_LOCK_RELEASE(curthread, &cv->c_lock); 180 181 /* Free the cond lock structure: */ 182 183 /* 184 * Free the memory allocated for the condition 185 * variable structure: 186 */ 187 free(cv); 188 189 } 190 /* Return the completion status: */ 191 return (rval); 192 } 193 194 struct cond_cancel_info { 195 TAILQ_ENTRY(cond_cancel_info) entry; 196 pthread_mutex_t *mutex; 197 pthread_cond_t *cond; 198 int count; 199 int queued; 200 }; 201 202 static void 203 cond_cancel_handler(void *arg) 204 { 205 struct pthread *curthread = tls_get_curthread(); 206 struct cond_cancel_info *info = (struct cond_cancel_info *)arg; 207 pthread_cond_t cv; 208 209 cv = *info->cond; 210 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 211 cond_log("cond_cancel %p\n", cv); 212 213 if (info->queued) { 214 info->queued = 0; 215 cond_log("cond_cancel %p: info %p\n", cv, info); 216 TAILQ_REMOVE(&cv->c_waitlist, info, entry); 217 _thr_umtx_wake(&info->queued, 0); 218 } 219 THR_LOCK_RELEASE(curthread, &cv->c_lock); 220 221 /* _mutex_cv_lock(info->mutex, info->count); */ 222 } 223 224 /* 225 * Wait for pthread_cond_t to be signaled. 226 * 227 * NOTE: EINTR is ignored and may not be returned by this function. 228 */ 229 static int 230 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 231 const struct timespec *abstime, int cancel) 232 { 233 struct pthread *curthread = tls_get_curthread(); 234 struct timespec ts, ts2, *tsp; 235 struct cond_cancel_info info; 236 pthread_cond_t cv; 237 int oldcancel; 238 int ret; 239 240 /* 241 * If the condition variable is statically initialized, 242 * perform the dynamic initialization: 243 */ 244 cond_log("cond_wait_common %p on mutex %p info %p\n", 245 *cond, *mutex, &info); 246 if (__predict_false(*cond == NULL && 247 (ret = init_static(curthread, cond)) != 0)) { 248 cond_log("cond_wait_common %p (failedA %d)\n", *cond, ret); 249 return (ret); 250 } 251 252 cv = *cond; 253 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 254 ret = _mutex_cv_unlock(mutex, &info.count); 255 if (ret) { 256 cond_log("cond_wait_common %p (failedB %d)\n", cv, ret); 257 THR_LOCK_RELEASE(curthread, &cv->c_lock); 258 return ret; 259 } 260 261 cpu_ccfence(); 262 info.mutex = mutex; 263 info.cond = cond; 264 info.queued = 1; 265 TAILQ_INSERT_TAIL(&cv->c_waitlist, &info, entry); 266 267 /* 268 * loop if we have never been told to wake up 269 * or we lost a race. 270 */ 271 while (info.queued) { 272 THR_LOCK_RELEASE(curthread, &cv->c_lock); 273 274 if (abstime != NULL) { 275 clock_gettime(cv->c_clockid, &ts); 276 TIMESPEC_SUB(&ts2, abstime, &ts); 277 tsp = &ts2; 278 } else { 279 tsp = NULL; 280 } 281 282 if (cancel) { 283 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); 284 oldcancel = _thr_cancel_enter(curthread); 285 ret = _thr_umtx_wait(&info.queued, 1, tsp, 286 cv->c_clockid); 287 _thr_cancel_leave(curthread, oldcancel); 288 THR_CLEANUP_POP(curthread, 0); 289 } else { 290 ret = _thr_umtx_wait(&info.queued, 1, tsp, 291 cv->c_clockid); 292 } 293 294 /* 295 * Ignore EINTR. Make sure ret is 0 if not ETIMEDOUT. 296 */ 297 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 298 if (abstime != NULL && ret == ETIMEDOUT) 299 break; 300 cpu_ccfence(); 301 } 302 303 if (info.queued) { 304 info.queued = 0; 305 TAILQ_REMOVE(&cv->c_waitlist, &info, entry); 306 ret = ETIMEDOUT; 307 } else { 308 ret = 0; 309 } 310 THR_LOCK_RELEASE(curthread, &cv->c_lock); 311 312 cond_log("cond_wait_common %p (doneA)\n", cv); 313 _mutex_cv_lock(mutex, info.count); 314 315 if (ret) 316 cond_log("cond_wait_common %p (failed %d)\n", cv, ret); 317 else 318 cond_log("cond_wait_common %p (doneB)\n", cv); 319 320 return (ret); 321 } 322 323 int 324 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 325 { 326 return (cond_wait_common(cond, mutex, NULL, 0)); 327 } 328 329 int 330 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 331 { 332 return (cond_wait_common(cond, mutex, NULL, 1)); 333 } 334 335 int 336 _pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 337 const struct timespec * abstime) 338 { 339 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 340 abstime->tv_nsec >= 1000000000) 341 return (EINVAL); 342 343 return (cond_wait_common(cond, mutex, abstime, 0)); 344 } 345 346 int 347 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 348 const struct timespec *abstime) 349 { 350 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 351 abstime->tv_nsec >= 1000000000) 352 return (EINVAL); 353 354 return (cond_wait_common(cond, mutex, abstime, 1)); 355 } 356 357 static int 358 cond_signal_common(pthread_cond_t *cond, int broadcast) 359 { 360 struct pthread *curthread = tls_get_curthread(); 361 struct cond_cancel_info *info; 362 pthread_cond_t cv; 363 int ret = 0; 364 365 cond_log("cond_signal_common %p broad=%d\n", *cond, broadcast); 366 367 /* 368 * If the condition variable is statically initialized, perform dynamic 369 * initialization. 370 */ 371 if (__predict_false(*cond == NULL && 372 (ret = init_static(curthread, cond)) != 0)) { 373 cond_log("cond_signal_common %p (failedA %d)\n", *cond, ret); 374 return (ret); 375 } 376 377 cv = *cond; 378 /* Lock the condition variable structure. */ 379 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 380 while ((info = TAILQ_FIRST(&cv->c_waitlist)) != NULL) { 381 info->queued = 0; 382 TAILQ_REMOVE(&cv->c_waitlist, info, entry); 383 cond_log("cond_signal_common %p: wakeup %p\n", *cond, info); 384 _thr_umtx_wake(&info->queued, 0); 385 if (broadcast == 0) 386 break; 387 } 388 THR_LOCK_RELEASE(curthread, &cv->c_lock); 389 390 if (ret) 391 cond_log("cond_signal_common %p (failedB %d)\n", *cond, ret); 392 else 393 cond_log("cond_signal_common %p (done)\n", *cond); 394 395 return (ret); 396 } 397 398 int 399 _pthread_cond_signal(pthread_cond_t * cond) 400 { 401 return (cond_signal_common(cond, 0)); 402 } 403 404 int 405 _pthread_cond_broadcast(pthread_cond_t * cond) 406 { 407 return (cond_signal_common(cond, 1)); 408 } 409 410 /* 411 * Double underscore versions are cancellation points. Single underscore 412 * versions are not and are provided for libc internal usage (which 413 * shouldn't introduce cancellation points). 414 */ 415 __strong_reference(__pthread_cond_wait, pthread_cond_wait); 416 __strong_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 417 418 __strong_reference(_pthread_cond_init, pthread_cond_init); 419 __strong_reference(_pthread_cond_destroy, pthread_cond_destroy); 420 __strong_reference(_pthread_cond_signal, pthread_cond_signal); 421 __strong_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 422