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 * __restrict cond, 147 const pthread_condattr_t * __restrict cond_attr) 148 { 149 *cond = NULL; 150 return cond_init(cond, cond_attr); 151 } 152 153 int 154 _pthread_cond_destroy(pthread_cond_t *cond) 155 { 156 struct pthread_cond *cv; 157 struct pthread *curthread = tls_get_curthread(); 158 int rval = 0; 159 160 if (cond == NULL) 161 rval = EINVAL; 162 else if (*cond == NULL) 163 rval = 0; 164 else { 165 /* Lock the condition variable structure: */ 166 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 167 if (TAILQ_FIRST(&(*cond)->c_waitlist)) { 168 THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 169 return (EBUSY); 170 } 171 172 /* 173 * NULL the caller's pointer now that the condition 174 * variable has been destroyed: 175 */ 176 cv = *cond; 177 *cond = NULL; 178 179 /* Unlock the condition variable structure: */ 180 THR_LOCK_RELEASE(curthread, &cv->c_lock); 181 182 /* Free the cond lock structure: */ 183 184 /* 185 * Free the memory allocated for the condition 186 * variable structure: 187 */ 188 free(cv); 189 190 } 191 /* Return the completion status: */ 192 return (rval); 193 } 194 195 struct cond_cancel_info { 196 TAILQ_ENTRY(cond_cancel_info) entry; 197 pthread_mutex_t *mutex; 198 pthread_cond_t *cond; 199 int count; 200 int queued; 201 }; 202 203 static void 204 cond_cancel_handler(void *arg) 205 { 206 struct pthread *curthread = tls_get_curthread(); 207 struct cond_cancel_info *info = (struct cond_cancel_info *)arg; 208 pthread_cond_t cv; 209 210 cv = *info->cond; 211 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 212 cond_log("cond_cancel %p\n", cv); 213 214 if (info->queued) { 215 info->queued = 0; 216 cond_log("cond_cancel %p: info %p\n", cv, info); 217 TAILQ_REMOVE(&cv->c_waitlist, info, entry); 218 _thr_umtx_wake(&info->queued, 0); 219 } 220 THR_LOCK_RELEASE(curthread, &cv->c_lock); 221 222 /* _mutex_cv_lock(info->mutex, info->count); */ 223 } 224 225 /* 226 * Wait for pthread_cond_t to be signaled. 227 * 228 * NOTE: EINTR is ignored and may not be returned by this function. 229 */ 230 static int 231 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 232 const struct timespec *abstime, int cancel) 233 { 234 struct pthread *curthread = tls_get_curthread(); 235 struct timespec ts, ts2, *tsp; 236 struct cond_cancel_info info; 237 pthread_cond_t cv; 238 int oldcancel; 239 int ret; 240 241 /* 242 * If the condition variable is statically initialized, 243 * perform the dynamic initialization: 244 */ 245 cond_log("cond_wait_common %p on mutex %p info %p\n", 246 *cond, *mutex, &info); 247 if (__predict_false(*cond == NULL && 248 (ret = init_static(curthread, cond)) != 0)) { 249 cond_log("cond_wait_common %p (failedA %d)\n", *cond, ret); 250 return (ret); 251 } 252 253 cv = *cond; 254 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 255 ret = _mutex_cv_unlock(mutex, &info.count); 256 if (ret) { 257 cond_log("cond_wait_common %p (failedB %d)\n", cv, ret); 258 THR_LOCK_RELEASE(curthread, &cv->c_lock); 259 return ret; 260 } 261 262 cpu_ccfence(); 263 info.mutex = mutex; 264 info.cond = cond; 265 info.queued = 1; 266 TAILQ_INSERT_TAIL(&cv->c_waitlist, &info, entry); 267 268 /* 269 * loop if we have never been told to wake up 270 * or we lost a race. 271 */ 272 while (info.queued) { 273 THR_LOCK_RELEASE(curthread, &cv->c_lock); 274 275 if (abstime != NULL) { 276 clock_gettime(cv->c_clockid, &ts); 277 TIMESPEC_SUB(&ts2, abstime, &ts); 278 tsp = &ts2; 279 } else { 280 tsp = NULL; 281 } 282 283 if (cancel) { 284 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); 285 oldcancel = _thr_cancel_enter(curthread); 286 ret = _thr_umtx_wait(&info.queued, 1, tsp, 287 cv->c_clockid); 288 _thr_cancel_leave(curthread, oldcancel); 289 THR_CLEANUP_POP(curthread, 0); 290 } else { 291 ret = _thr_umtx_wait(&info.queued, 1, tsp, 292 cv->c_clockid); 293 } 294 295 /* 296 * Ignore EINTR. Make sure ret is 0 if not ETIMEDOUT. 297 */ 298 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 299 if (abstime != NULL && ret == ETIMEDOUT) 300 break; 301 cpu_ccfence(); 302 } 303 304 if (info.queued) { 305 info.queued = 0; 306 TAILQ_REMOVE(&cv->c_waitlist, &info, entry); 307 ret = ETIMEDOUT; 308 } else { 309 ret = 0; 310 } 311 THR_LOCK_RELEASE(curthread, &cv->c_lock); 312 313 cond_log("cond_wait_common %p (doneA)\n", cv); 314 _mutex_cv_lock(mutex, info.count); 315 316 if (ret) 317 cond_log("cond_wait_common %p (failed %d)\n", cv, ret); 318 else 319 cond_log("cond_wait_common %p (doneB)\n", cv); 320 321 return (ret); 322 } 323 324 int 325 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 326 { 327 return (cond_wait_common(cond, mutex, NULL, 0)); 328 } 329 330 int 331 __pthread_cond_wait(pthread_cond_t * __restrict cond, 332 pthread_mutex_t * __restrict mutex) 333 { 334 return (cond_wait_common(cond, mutex, NULL, 1)); 335 } 336 337 int 338 _pthread_cond_timedwait(pthread_cond_t * __restrict cond, 339 pthread_mutex_t * __restrict mutex, 340 const struct timespec * __restrict abstime) 341 { 342 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 343 abstime->tv_nsec >= 1000000000) 344 return (EINVAL); 345 346 return (cond_wait_common(cond, mutex, abstime, 0)); 347 } 348 349 int 350 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 351 const struct timespec *abstime) 352 { 353 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 354 abstime->tv_nsec >= 1000000000) 355 return (EINVAL); 356 357 return (cond_wait_common(cond, mutex, abstime, 1)); 358 } 359 360 static int 361 cond_signal_common(pthread_cond_t *cond, int broadcast) 362 { 363 struct pthread *curthread = tls_get_curthread(); 364 struct cond_cancel_info *info; 365 pthread_cond_t cv; 366 int ret = 0; 367 368 cond_log("cond_signal_common %p broad=%d\n", *cond, broadcast); 369 370 /* 371 * If the condition variable is statically initialized, perform dynamic 372 * initialization. 373 */ 374 if (__predict_false(*cond == NULL && 375 (ret = init_static(curthread, cond)) != 0)) { 376 cond_log("cond_signal_common %p (failedA %d)\n", *cond, ret); 377 return (ret); 378 } 379 380 cv = *cond; 381 /* Lock the condition variable structure. */ 382 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 383 while ((info = TAILQ_FIRST(&cv->c_waitlist)) != NULL) { 384 info->queued = 0; 385 TAILQ_REMOVE(&cv->c_waitlist, info, entry); 386 cond_log("cond_signal_common %p: wakeup %p\n", *cond, info); 387 _thr_umtx_wake(&info->queued, 0); 388 if (broadcast == 0) 389 break; 390 } 391 THR_LOCK_RELEASE(curthread, &cv->c_lock); 392 393 if (ret) 394 cond_log("cond_signal_common %p (failedB %d)\n", *cond, ret); 395 else 396 cond_log("cond_signal_common %p (done)\n", *cond); 397 398 return (ret); 399 } 400 401 int 402 _pthread_cond_signal(pthread_cond_t * cond) 403 { 404 return (cond_signal_common(cond, 0)); 405 } 406 407 int 408 _pthread_cond_broadcast(pthread_cond_t * cond) 409 { 410 return (cond_signal_common(cond, 1)); 411 } 412 413 /* 414 * Double underscore versions are cancellation points. Single underscore 415 * versions are not and are provided for libc internal usage (which 416 * shouldn't introduce cancellation points). 417 */ 418 __strong_reference(__pthread_cond_wait, pthread_cond_wait); 419 __strong_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 420 421 __strong_reference(_pthread_cond_init, pthread_cond_init); 422 __strong_reference(_pthread_cond_destroy, pthread_cond_destroy); 423 __strong_reference(_pthread_cond_signal, pthread_cond_signal); 424 __strong_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 425