thr_cond.c revision 115198
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 115198 2003-05-21 03:41:07Z 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 * Prototypes 42 */ 43static pthread_t cond_queue_deq(pthread_cond_t); 44static void cond_queue_remove(pthread_cond_t, pthread_t); 45static void cond_queue_enq(pthread_cond_t, pthread_t); 46static int cond_wait_common(pthread_cond_t *, 47 pthread_mutex_t *, const struct timespec *); 48 49__weak_reference(_pthread_cond_init, pthread_cond_init); 50__weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 51__weak_reference(_pthread_cond_wait, pthread_cond_wait); 52__weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait); 53__weak_reference(_pthread_cond_signal, pthread_cond_signal); 54__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 55 56#define COND_LOCK(c) \ 57do { \ 58 if (umtx_lock(&(c)->c_lock, curthread->thr_id)) \ 59 abort(); \ 60} while (0) 61 62#define COND_UNLOCK(c) \ 63do { \ 64 if (umtx_unlock(&(c)->c_lock, curthread->thr_id)) \ 65 abort(); \ 66} while (0) 67 68 69/* Reinitialize a condition variable to defaults. */ 70int 71_cond_reinit(pthread_cond_t *cond) 72{ 73 if (cond == NULL) 74 return (EINVAL); 75 76 if (*cond == NULL) 77 return (pthread_cond_init(cond, NULL)); 78 79 /* 80 * Initialize the condition variable structure: 81 */ 82 TAILQ_INIT(&(*cond)->c_queue); 83 (*cond)->c_flags = COND_FLAGS_INITED; 84 (*cond)->c_type = COND_TYPE_FAST; 85 (*cond)->c_mutex = NULL; 86 (*cond)->c_seqno = 0; 87 bzero(&(*cond)->c_lock, sizeof((*cond)->c_lock)); 88 89 return (0); 90} 91 92int 93_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 94{ 95 enum pthread_cond_type type; 96 pthread_cond_t pcond; 97 98 if (cond == NULL) 99 return (EINVAL); 100 101 /* 102 * Check if a pointer to a condition variable attribute 103 * structure was passed by the caller: 104 */ 105 if (cond_attr != NULL && *cond_attr != NULL) 106 type = (*cond_attr)->c_type; 107 else 108 /* Default to a fast condition variable: */ 109 type = COND_TYPE_FAST; 110 111 /* Process according to condition variable type: */ 112 switch (type) { 113 case COND_TYPE_FAST: 114 break; 115 default: 116 return (EINVAL); 117 break; 118 } 119 120 if ((pcond = (pthread_cond_t) 121 malloc(sizeof(struct pthread_cond))) == NULL) 122 return (ENOMEM); 123 /* 124 * Initialise the condition variable 125 * structure: 126 */ 127 TAILQ_INIT(&pcond->c_queue); 128 pcond->c_flags |= COND_FLAGS_INITED; 129 pcond->c_type = type; 130 pcond->c_mutex = NULL; 131 pcond->c_seqno = 0; 132 bzero(&pcond->c_lock, sizeof(pcond->c_lock)); 133 134 *cond = pcond; 135 136 return (0); 137} 138 139int 140_pthread_cond_destroy(pthread_cond_t *cond) 141{ 142 if (cond == NULL || *cond == NULL) 143 return (EINVAL); 144 145 COND_LOCK(*cond); 146 147 /* 148 * Free the memory allocated for the condition 149 * variable structure: 150 */ 151 free(*cond); 152 153 /* 154 * NULL the caller's pointer now that the condition 155 * variable has been destroyed: 156 */ 157 *cond = NULL; 158 159 return (0); 160} 161 162int 163_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 164{ 165 int rval; 166 167 rval = cond_wait_common(cond, mutex, NULL); 168 169 /* This should never happen. */ 170 if (rval == ETIMEDOUT) 171 abort(); 172 173 return (rval); 174} 175 176int 177_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 178 const struct timespec * abstime) 179{ 180 if (abstime == NULL || abstime->tv_nsec >= 1000000000) 181 return (EINVAL); 182 183 return (cond_wait_common(cond, mutex, abstime)); 184} 185 186static int 187cond_wait_common(pthread_cond_t * cond, pthread_mutex_t * mutex, 188 const struct timespec * abstime) 189{ 190 int rval = 0; 191 int done = 0; 192 int seqno; 193 int mtxrval; 194 195 196 _thread_enter_cancellation_point(); 197 198 /* 199 * If the condition variable is statically initialized, perform dynamic 200 * initialization. 201 */ 202 if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0) 203 return (rval); 204 205 206 COND_LOCK(*cond); 207 208 /* 209 * If the condvar was statically allocated, properly 210 * initialize the tail queue. 211 */ 212 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 213 TAILQ_INIT(&(*cond)->c_queue); 214 (*cond)->c_flags |= COND_FLAGS_INITED; 215 } 216 217 /* Process according to condition variable type. */ 218 219 switch ((*cond)->c_type) { 220 /* Fast condition variable: */ 221 case COND_TYPE_FAST: 222 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 223 ((*cond)->c_mutex != *mutex))) { 224 COND_UNLOCK(*cond); 225 rval = EINVAL; 226 break; 227 } 228 /* Remember the mutex */ 229 (*cond)->c_mutex = *mutex; 230 231 if ((rval = _mutex_cv_unlock(mutex)) != 0) { 232 if (rval == -1){ 233 printf("foo"); 234 fflush(stdout); 235 abort(); 236 } 237 238 COND_UNLOCK(*cond); 239 break; 240 } 241 COND_UNLOCK(*cond); 242 243 /* 244 * We need giant for the queue operations. It also 245 * protects seqno and the pthread flag fields. This is 246 * dropped and reacquired in _thread_suspend(). 247 */ 248 249 GIANT_LOCK(curthread); 250 /* 251 * c_seqno is protected by giant. 252 */ 253 seqno = (*cond)->c_seqno; 254 255 do { 256 /* 257 * Queue the running thread on the condition 258 * variable. 259 */ 260 cond_queue_enq(*cond, curthread); 261 262 if (curthread->cancelflags & PTHREAD_CANCELLING) { 263 /* 264 * POSIX Says that we must relock the mutex 265 * even if we're being canceled. 266 */ 267 GIANT_UNLOCK(curthread); 268 _mutex_cv_lock(mutex); 269 pthread_testcancel(); 270 PANIC("Shouldn't have come back."); 271 } 272 273 PTHREAD_SET_STATE(curthread, PS_COND_WAIT); 274 GIANT_UNLOCK(curthread); 275 rval = _thread_suspend(curthread, (struct timespec *)abstime); 276 if (rval == -1) { 277 printf("foo"); 278 fflush(stdout); 279 abort(); 280 } 281 GIANT_LOCK(curthread); 282 283 done = (seqno != (*cond)->c_seqno); 284 285 cond_queue_remove(*cond, curthread); 286 287 } while ((done == 0) && (rval == 0)); 288 /* 289 * If we timed out someone still may have signaled us 290 * before we got a chance to run again. We check for 291 * this by looking to see if our state is RUNNING. 292 */ 293 if (rval == EAGAIN) { 294 if (curthread->state != PS_RUNNING) { 295 PTHREAD_SET_STATE(curthread, PS_RUNNING); 296 rval = ETIMEDOUT; 297 } else 298 rval = 0; 299 } 300 GIANT_UNLOCK(curthread); 301 302 mtxrval = _mutex_cv_lock(mutex); 303 304 /* 305 * If the mutex failed return that error, otherwise we're 306 * returning ETIMEDOUT. 307 */ 308 if (mtxrval == -1) { 309 printf("foo"); 310 fflush(stdout); 311 abort(); 312 } 313 if (mtxrval != 0) 314 rval = mtxrval; 315 316 break; 317 318 /* Trap invalid condition variable types: */ 319 default: 320 COND_UNLOCK(*cond); 321 rval = EINVAL; 322 break; 323 } 324 325 /* 326 * See if we have to cancel before we retry. We could be 327 * canceled with the mutex held here! 328 */ 329 pthread_testcancel(); 330 331 _thread_leave_cancellation_point(); 332 333 return (rval); 334} 335 336int 337_pthread_cond_signal(pthread_cond_t * cond) 338{ 339 int rval = 0; 340 pthread_t pthread; 341 342 if (cond == NULL) 343 return (EINVAL); 344 /* 345 * If the condition variable is statically initialized, perform dynamic 346 * initialization. 347 */ 348 if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0) 349 return (rval); 350 351 352 COND_LOCK(*cond); 353 354 /* Process according to condition variable type: */ 355 switch ((*cond)->c_type) { 356 /* Fast condition variable: */ 357 case COND_TYPE_FAST: 358 GIANT_LOCK(curthread); 359 (*cond)->c_seqno++; 360 361 if ((pthread = cond_queue_deq(*cond)) != NULL) { 362 /* 363 * Wake up the signaled thread: 364 */ 365 PTHREAD_NEW_STATE(pthread, PS_RUNNING); 366 } 367 368 GIANT_UNLOCK(curthread); 369 break; 370 371 /* Trap invalid condition variable types: */ 372 default: 373 rval = EINVAL; 374 break; 375 } 376 377 378 COND_UNLOCK(*cond); 379 380 return (rval); 381} 382 383int 384_pthread_cond_broadcast(pthread_cond_t * cond) 385{ 386 int rval = 0; 387 pthread_t pthread; 388 389 if (cond == NULL) 390 return (EINVAL); 391 /* 392 * If the condition variable is statically initialized, perform dynamic 393 * initialization. 394 */ 395 if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0) 396 return (rval); 397 398 COND_LOCK(*cond); 399 400 /* Process according to condition variable type: */ 401 switch ((*cond)->c_type) { 402 /* Fast condition variable: */ 403 case COND_TYPE_FAST: 404 GIANT_LOCK(curthread); 405 (*cond)->c_seqno++; 406 407 /* 408 * Enter a loop to bring all threads off the 409 * condition queue: 410 */ 411 while ((pthread = cond_queue_deq(*cond)) != NULL) { 412 /* 413 * Wake up the signaled thread: 414 */ 415 PTHREAD_NEW_STATE(pthread, PS_RUNNING); 416 } 417 GIANT_UNLOCK(curthread); 418 419 /* There are no more waiting threads: */ 420 (*cond)->c_mutex = NULL; 421 422 break; 423 424 /* Trap invalid condition variable types: */ 425 default: 426 rval = EINVAL; 427 break; 428 } 429 430 COND_UNLOCK(*cond); 431 432 433 return (rval); 434} 435 436void 437_cond_wait_backout(pthread_t pthread) 438{ 439 pthread_cond_t cond; 440 441 cond = pthread->data.cond; 442 if (cond == NULL) 443 return; 444 445 COND_LOCK(cond); 446 447 /* Process according to condition variable type: */ 448 switch (cond->c_type) { 449 /* Fast condition variable: */ 450 case COND_TYPE_FAST: 451 GIANT_LOCK(curthread); 452 453 cond_queue_remove(cond, pthread); 454 455 GIANT_UNLOCK(curthread); 456 break; 457 458 default: 459 break; 460 } 461 462 COND_UNLOCK(cond); 463} 464 465/* 466 * Dequeue a waiting thread from the head of a condition queue in 467 * descending priority order. 468 */ 469static pthread_t 470cond_queue_deq(pthread_cond_t cond) 471{ 472 pthread_t pthread; 473 474 while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) { 475 TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 476 cond_queue_remove(cond, pthread); 477 if ((pthread->cancelflags & PTHREAD_CANCELLING) == 0 && 478 pthread->state == PS_COND_WAIT) 479 /* 480 * Only exit the loop when we find a thread 481 * that hasn't timed out or been canceled; 482 * those threads are already running and don't 483 * need their run state changed. 484 */ 485 break; 486 } 487 488 return(pthread); 489} 490 491/* 492 * Remove a waiting thread from a condition queue in descending priority 493 * order. 494 */ 495static void 496cond_queue_remove(pthread_cond_t cond, pthread_t pthread) 497{ 498 /* 499 * Because pthread_cond_timedwait() can timeout as well 500 * as be signaled by another thread, it is necessary to 501 * guard against removing the thread from the queue if 502 * it isn't in the queue. 503 */ 504 if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) { 505 TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 506 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ; 507 } 508 /* Check for no more waiters. */ 509 if (TAILQ_FIRST(&cond->c_queue) == NULL) 510 cond->c_mutex = NULL; 511} 512 513/* 514 * Enqueue a waiting thread to a condition queue in descending priority 515 * order. 516 */ 517static void 518cond_queue_enq(pthread_cond_t cond, pthread_t pthread) 519{ 520 pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head); 521 char *name; 522 523 name = pthread->name ? pthread->name : "unknown"; 524 if ((pthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0) 525 _thread_printf(2, "Thread (%s:%u) already on condq\n", 526 pthread->name, pthread->uniqueid); 527 if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) 528 _thread_printf(2, "Thread (%s:%u) already on mutexq\n", 529 pthread->name, pthread->uniqueid); 530 PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread); 531 532 /* 533 * For the common case of all threads having equal priority, 534 * we perform a quick check against the priority of the thread 535 * at the tail of the queue. 536 */ 537 if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) 538 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe); 539 else { 540 tid = TAILQ_FIRST(&cond->c_queue); 541 while (pthread->active_priority <= tid->active_priority) 542 tid = TAILQ_NEXT(tid, sqe); 543 TAILQ_INSERT_BEFORE(tid, pthread, sqe); 544 } 545 pthread->flags |= PTHREAD_FLAGS_IN_CONDQ; 546 pthread->data.cond = cond; 547} 548