thr_cond.c revision 129484
1/* 2 * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. 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, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by John Birrell. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $FreeBSD: head/lib/libthr/thread/thr_cond.c 129484 2004-05-20 12:06:16Z mtm $ 33 */ 34#include <stdlib.h> 35#include <errno.h> 36#include <string.h> 37#include <pthread.h> 38#include "thr_private.h" 39 40/* 41 * Proctect two different threads calling a pthread_cond_* function 42 * from accidentally initializing the condition variable twice. 43 */ 44static spinlock_t static_cond_lock = _SPINLOCK_INITIALIZER; 45 46/* 47 * Prototypes 48 */ 49static inline int cond_init(pthread_cond_t *); 50static pthread_t cond_queue_deq(pthread_cond_t); 51static void cond_queue_remove(pthread_cond_t, pthread_t); 52static void cond_queue_enq(pthread_cond_t, pthread_t); 53static int cond_signal(pthread_cond_t *, int); 54static int cond_wait_common(pthread_cond_t *, 55 pthread_mutex_t *, const struct timespec *); 56 57__weak_reference(_pthread_cond_init, pthread_cond_init); 58__weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 59__weak_reference(_pthread_cond_wait, pthread_cond_wait); 60__weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait); 61__weak_reference(_pthread_cond_signal, pthread_cond_signal); 62__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 63 64#define COND_LOCK(c) \ 65do { \ 66 if (umtx_lock(&(c)->c_lock, curthread->thr_id)) \ 67 abort(); \ 68} while (0) 69 70#define COND_UNLOCK(c) \ 71do { \ 72 if (umtx_unlock(&(c)->c_lock, curthread->thr_id)) \ 73 abort(); \ 74} while (0) 75 76 77/* Reinitialize a condition variable to defaults. */ 78int 79_cond_reinit(pthread_cond_t *cond) 80{ 81 if (cond == NULL) 82 return (EINVAL); 83 84 if (*cond == NULL) 85 return (pthread_cond_init(cond, NULL)); 86 87 /* 88 * Initialize the condition variable structure: 89 */ 90 TAILQ_INIT(&(*cond)->c_queue); 91 (*cond)->c_flags = COND_FLAGS_INITED; 92 (*cond)->c_type = COND_TYPE_FAST; 93 (*cond)->c_mutex = NULL; 94 (*cond)->c_seqno = 0; 95 bzero(&(*cond)->c_lock, sizeof((*cond)->c_lock)); 96 97 return (0); 98} 99 100int 101_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 102{ 103 enum pthread_cond_type type; 104 pthread_cond_t pcond; 105 106 if (cond == NULL) 107 return (EINVAL); 108 109 /* 110 * Check if a pointer to a condition variable attribute 111 * structure was passed by the caller: 112 */ 113 if (cond_attr != NULL && *cond_attr != NULL) 114 type = (*cond_attr)->c_type; 115 else 116 /* Default to a fast condition variable: */ 117 type = COND_TYPE_FAST; 118 119 /* Process according to condition variable type: */ 120 switch (type) { 121 case COND_TYPE_FAST: 122 break; 123 default: 124 return (EINVAL); 125 break; 126 } 127 128 if ((pcond = (pthread_cond_t) 129 malloc(sizeof(struct pthread_cond))) == NULL) 130 return (ENOMEM); 131 /* 132 * Initialise the condition variable 133 * structure: 134 */ 135 TAILQ_INIT(&pcond->c_queue); 136 pcond->c_flags |= COND_FLAGS_INITED; 137 pcond->c_type = type; 138 pcond->c_mutex = NULL; 139 pcond->c_seqno = 0; 140 bzero(&pcond->c_lock, sizeof(pcond->c_lock)); 141 142 *cond = pcond; 143 144 return (0); 145} 146 147int 148_pthread_cond_destroy(pthread_cond_t *cond) 149{ 150 /* 151 * Short circuit for a statically initialized condvar 152 * that is being destroyed without having been used. 153 */ 154 if (*cond == PTHREAD_COND_INITIALIZER) 155 return (0); 156 157 COND_LOCK(*cond); 158 159 /* 160 * Free the memory allocated for the condition 161 * variable structure: 162 */ 163 free(*cond); 164 165 /* 166 * NULL the caller's pointer now that the condition 167 * variable has been destroyed: 168 */ 169 *cond = NULL; 170 171 return (0); 172} 173 174int 175_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 176{ 177 int rval; 178 179 rval = cond_wait_common(cond, mutex, NULL); 180 181 /* This should never happen. */ 182 if (rval == ETIMEDOUT) 183 abort(); 184 185 return (rval); 186} 187 188int 189_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 190 const struct timespec * abstime) 191{ 192 if (abstime == NULL || abstime->tv_nsec >= 1000000000) 193 return (EINVAL); 194 195 return (cond_wait_common(cond, mutex, abstime)); 196} 197 198static int 199cond_wait_common(pthread_cond_t * cond, pthread_mutex_t * mutex, 200 const struct timespec * abstime) 201{ 202 int rval = 0; 203 int mtxrval; 204 205 206 if (cond == NULL) 207 return (EINVAL); 208 /* 209 * If the condition variable is statically initialized, perform dynamic 210 * initialization. 211 */ 212 if (*cond == PTHREAD_COND_INITIALIZER && (rval = cond_init(cond)) != 0) 213 return (rval); 214 215 if ((*cond)->c_type != COND_TYPE_FAST) 216 return (EINVAL); 217 218 COND_LOCK(*cond); 219 220 /* 221 * If the condvar was statically allocated, properly 222 * initialize the tail queue. 223 */ 224 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 225 TAILQ_INIT(&(*cond)->c_queue); 226 (*cond)->c_flags |= COND_FLAGS_INITED; 227 } 228 229 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 230 ((*cond)->c_mutex != *mutex))) { 231 COND_UNLOCK(*cond); 232 return (EINVAL); 233 } 234 /* Remember the mutex */ 235 (*cond)->c_mutex = *mutex; 236 237 _thread_enter_cancellation_point(); 238 if ((rval = _mutex_cv_unlock(mutex)) != 0) { 239 if (rval == -1){ 240 printf("mutex unlock by condvar failed!"); 241 fflush(stdout); 242 abort(); 243 } 244 _thread_leave_cancellation_point(); 245 COND_UNLOCK(*cond); 246 return (rval); 247 } 248 249 /* 250 * We need to protect the queue operations. It also 251 * protects the pthread flag field. This is 252 * dropped before calling _thread_suspend() and reaquired 253 * when we return. 254 */ 255 PTHREAD_LOCK(curthread); 256 257 /* 258 * Queue the running thread on the condition 259 * variable and wait to be signaled. 260 */ 261 cond_queue_enq(*cond, curthread); 262 do { 263 PTHREAD_UNLOCK(curthread); 264 COND_UNLOCK(*cond); 265 if (curthread->cancellation == CS_PENDING) { 266 /* 267 * Posix says we must lock the mutex 268 * even if we're being canceled. 269 */ 270 _mutex_cv_lock(mutex); 271 _thread_leave_cancellation_point(); 272 PANIC("Shouldn't have come back."); 273 } 274 rval = _thread_suspend(curthread, (struct timespec *)abstime); 275 if (rval != 0 && rval != ETIMEDOUT && rval != EINTR) { 276 printf("thread suspend returned an invalid value"); 277 fflush(stdout); 278 abort(); 279 } 280 COND_LOCK(*cond); 281 PTHREAD_LOCK(curthread); 282 if (rval == ETIMEDOUT) { 283 /* 284 * Condition may have been signaled between the 285 * time the thread timed out and locked the condvar. 286 * If it wasn't, manually remove it from the queue. 287 */ 288 if ((curthread->flags & PTHREAD_FLAGS_IN_CONDQ) == 0) 289 rval = 0; 290 else 291 cond_queue_remove(*cond, curthread); 292 } 293 } while ((curthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0); 294 295 PTHREAD_UNLOCK(curthread); 296 COND_UNLOCK(*cond); 297 mtxrval = _mutex_cv_lock(mutex); 298 299 /* If the mutex failed return that error. */ 300 if (mtxrval == -1) { 301 printf("mutex lock from condvar failed!"); 302 fflush(stdout); 303 abort(); 304 } 305 if (mtxrval != 0) 306 rval = mtxrval; 307 308 _thread_leave_cancellation_point(); 309 return (rval); 310} 311 312int 313_pthread_cond_signal(pthread_cond_t * cond) 314{ 315 return (cond_signal(cond, 0)); 316} 317 318int 319_pthread_cond_broadcast(pthread_cond_t * cond) 320{ 321 return (cond_signal(cond, 1)); 322} 323 324static int 325cond_signal(pthread_cond_t * cond, int broadcast) 326{ 327 int rval = 0; 328 pthread_t pthread; 329 330 if (cond == NULL) 331 return (EINVAL); 332 /* 333 * If the condition variable is statically initialized, perform dynamic 334 * initialization. 335 */ 336 if (*cond == PTHREAD_COND_INITIALIZER && (rval = cond_init(cond)) != 0) 337 return (rval); 338 339 if ((*cond)->c_type != COND_TYPE_FAST) 340 return (EINVAL); 341 COND_LOCK(*cond); 342 343 /* 344 * Enter a loop to bring all (or only one) threads off the 345 * condition queue: 346 */ 347 do { 348 /* 349 * Wake up the signaled thread. It will be returned 350 * to us locked. 351 */ 352 if ((pthread = cond_queue_deq(*cond)) != NULL) { 353 PTHREAD_WAKE(pthread); 354 PTHREAD_UNLOCK(pthread); 355 } 356 } while (broadcast && pthread != NULL); 357 358 COND_UNLOCK(*cond); 359 return (rval); 360} 361 362void 363_cond_wait_backout(pthread_t pthread) 364{ 365 pthread_cond_t cond; 366 367 cond = pthread->data.cond; 368 if (cond == NULL) 369 return; 370 371 /* Process according to condition variable type: */ 372 switch (cond->c_type) { 373 /* Fast condition variable: */ 374 case COND_TYPE_FAST: 375 cond_queue_remove(cond, pthread); 376 break; 377 default: 378 break; 379 } 380} 381 382/* 383 * Dequeue a waiting thread from the head of a condition queue in 384 * descending priority order. 385 */ 386static pthread_t 387cond_queue_deq(pthread_cond_t cond) 388{ 389 pthread_t pthread; 390 391 while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) { 392 PTHREAD_LOCK(pthread); 393 cond_queue_remove(cond, pthread); 394 395 /* 396 * Only exit the loop when we find a thread 397 * that hasn't been canceled. 398 */ 399 if (pthread->cancellation == CS_NULL) 400 break; 401 else 402 PTHREAD_UNLOCK(pthread); 403 } 404 405 return(pthread); 406} 407 408/* 409 * Remove a waiting thread from a condition queue in descending priority 410 * order. 411 */ 412static void 413cond_queue_remove(pthread_cond_t cond, pthread_t pthread) 414{ 415 /* 416 * Because pthread_cond_timedwait() can timeout as well 417 * as be signaled by another thread, it is necessary to 418 * guard against removing the thread from the queue if 419 * it isn't in the queue. 420 */ 421 if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) { 422 TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 423 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ; 424 } 425 /* Check for no more waiters. */ 426 if (TAILQ_FIRST(&cond->c_queue) == NULL) 427 cond->c_mutex = NULL; 428} 429 430/* 431 * Enqueue a waiting thread to a condition queue in descending priority 432 * order. 433 */ 434static void 435cond_queue_enq(pthread_cond_t cond, pthread_t pthread) 436{ 437 pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head); 438 char *name; 439 440 name = pthread->name ? pthread->name : "unknown"; 441 if ((pthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0) 442 _thread_printf(2, "Thread (%s:%u) already on condq\n", 443 pthread->name, pthread->uniqueid); 444 if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) 445 _thread_printf(2, "Thread (%s:%u) already on mutexq\n", 446 pthread->name, pthread->uniqueid); 447 PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread); 448 449 /* 450 * For the common case of all threads having equal priority, 451 * we perform a quick check against the priority of the thread 452 * at the tail of the queue. 453 */ 454 if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) 455 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe); 456 else { 457 tid = TAILQ_FIRST(&cond->c_queue); 458 while (pthread->active_priority <= tid->active_priority) 459 tid = TAILQ_NEXT(tid, sqe); 460 TAILQ_INSERT_BEFORE(tid, pthread, sqe); 461 } 462 pthread->flags |= PTHREAD_FLAGS_IN_CONDQ; 463 pthread->data.cond = cond; 464} 465 466static inline int 467cond_init(pthread_cond_t *cond) 468{ 469 int error = 0; 470 _SPINLOCK(&static_cond_lock); 471 if (*cond == PTHREAD_COND_INITIALIZER) 472 error = _pthread_cond_init(cond, NULL); 473 _SPINUNLOCK(&static_cond_lock); 474 return (error); 475} 476 477