thr_cond.c revision 63364
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/libkse/thread/thr_cond.c 63364 2000-07-18 01:38:19Z jasone $ 33 */ 34#include <stdlib.h> 35#include <errno.h> 36#include <string.h> 37#ifdef _THREAD_SAFE 38#include <pthread.h> 39#include "pthread_private.h" 40 41/* 42 * Prototypes 43 */ 44static inline pthread_t cond_queue_deq(pthread_cond_t); 45static inline void cond_queue_remove(pthread_cond_t, pthread_t); 46static inline void cond_queue_enq(pthread_cond_t, pthread_t); 47 48/* Reinitialize a condition variable to defaults. */ 49int 50_cond_reinit(pthread_cond_t * cond) 51{ 52 int ret = 0; 53 54 if (cond == NULL) 55 ret = EINVAL; 56 else if (*cond == NULL) 57 ret = pthread_cond_init(cond, NULL); 58 else { 59 /* 60 * Initialize the condition variable structure: 61 */ 62 TAILQ_INIT(&(*cond)->c_queue); 63 (*cond)->c_flags = COND_FLAGS_INITED; 64 (*cond)->c_type = COND_TYPE_FAST; 65 (*cond)->c_mutex = NULL; 66 memset(&(*cond)->lock, 0, sizeof((*cond)->lock)); 67 } 68 return (ret); 69} 70 71int 72pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr) 73{ 74 enum pthread_cond_type type; 75 pthread_cond_t pcond; 76 int rval = 0; 77 78 if (cond == NULL) 79 rval = EINVAL; 80 else { 81 /* 82 * Check if a pointer to a condition variable attribute 83 * structure was passed by the caller: 84 */ 85 if (cond_attr != NULL && *cond_attr != NULL) { 86 /* Default to a fast condition variable: */ 87 type = (*cond_attr)->c_type; 88 } else { 89 /* Default to a fast condition variable: */ 90 type = COND_TYPE_FAST; 91 } 92 93 /* Process according to condition variable type: */ 94 switch (type) { 95 /* Fast condition variable: */ 96 case COND_TYPE_FAST: 97 /* Nothing to do here. */ 98 break; 99 100 /* Trap invalid condition variable types: */ 101 default: 102 /* Return an invalid argument error: */ 103 rval = EINVAL; 104 break; 105 } 106 107 /* Check for no errors: */ 108 if (rval == 0) { 109 if ((pcond = (pthread_cond_t) 110 malloc(sizeof(struct pthread_cond))) == NULL) { 111 rval = ENOMEM; 112 } else { 113 /* 114 * Initialise the condition variable 115 * structure: 116 */ 117 TAILQ_INIT(&pcond->c_queue); 118 pcond->c_flags |= COND_FLAGS_INITED; 119 pcond->c_type = type; 120 pcond->c_mutex = NULL; 121 memset(&pcond->lock,0,sizeof(pcond->lock)); 122 *cond = pcond; 123 } 124 } 125 } 126 /* Return the completion status: */ 127 return (rval); 128} 129 130int 131pthread_cond_destroy(pthread_cond_t * cond) 132{ 133 int rval = 0; 134 135 if (cond == NULL || *cond == NULL) 136 rval = EINVAL; 137 else { 138 /* Lock the condition variable structure: */ 139 _SPINLOCK(&(*cond)->lock); 140 141 /* 142 * Free the memory allocated for the condition 143 * variable structure: 144 */ 145 free(*cond); 146 147 /* 148 * NULL the caller's pointer now that the condition 149 * variable has been destroyed: 150 */ 151 *cond = NULL; 152 } 153 /* Return the completion status: */ 154 return (rval); 155} 156 157int 158pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) 159{ 160 int rval = 0; 161 int interrupted = 0; 162 163 _thread_enter_cancellation_point(); 164 165 if (cond == NULL) 166 rval = EINVAL; 167 168 /* 169 * If the condition variable is statically initialized, 170 * perform the dynamic initialization: 171 */ 172 else if (*cond != NULL || 173 (rval = pthread_cond_init(cond,NULL)) == 0) { 174 175 _thread_enter_cancellation_point(); 176 177 /* Lock the condition variable structure: */ 178 _SPINLOCK(&(*cond)->lock); 179 180 /* 181 * If the condvar was statically allocated, properly 182 * initialize the tail queue. 183 */ 184 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 185 TAILQ_INIT(&(*cond)->c_queue); 186 (*cond)->c_flags |= COND_FLAGS_INITED; 187 } 188 189 /* Process according to condition variable type: */ 190 switch ((*cond)->c_type) { 191 /* Fast condition variable: */ 192 case COND_TYPE_FAST: 193 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 194 ((*cond)->c_mutex != *mutex))) { 195 /* Unlock the condition variable structure: */ 196 _SPINUNLOCK(&(*cond)->lock); 197 198 /* Return invalid argument error: */ 199 rval = EINVAL; 200 } else { 201 /* Reset the timeout and interrupted flags: */ 202 _thread_run->timeout = 0; 203 _thread_run->interrupted = 0; 204 205 /* 206 * Queue the running thread for the condition 207 * variable: 208 */ 209 cond_queue_enq(*cond, _thread_run); 210 211 /* Remember the mutex that is being used: */ 212 (*cond)->c_mutex = *mutex; 213 214 /* Wait forever: */ 215 _thread_run->wakeup_time.tv_sec = -1; 216 217 /* Unlock the mutex: */ 218 if ((rval = _mutex_cv_unlock(mutex)) != 0) { 219 /* 220 * Cannot unlock the mutex, so remove 221 * the running thread from the condition 222 * variable queue: 223 */ 224 cond_queue_remove(*cond, _thread_run); 225 226 /* Check for no more waiters: */ 227 if (TAILQ_FIRST(&(*cond)->c_queue) == 228 NULL) 229 (*cond)->c_mutex = NULL; 230 231 /* Unlock the condition variable structure: */ 232 _SPINUNLOCK(&(*cond)->lock); 233 } 234 else { 235 /* 236 * Schedule the next thread and unlock 237 * the condition variable structure: 238 */ 239 _thread_kern_sched_state_unlock(PS_COND_WAIT, 240 &(*cond)->lock, __FILE__, __LINE__); 241 242 if (_thread_run->interrupted != 0) { 243 /* 244 * Remember that this thread 245 * was interrupted: 246 */ 247 interrupted = 1; 248 249 /* 250 * Lock the condition variable 251 * while removing the thread. 252 */ 253 _SPINLOCK(&(*cond)->lock); 254 255 cond_queue_remove(*cond, 256 _thread_run); 257 258 /* Check for no more waiters: */ 259 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 260 (*cond)->c_mutex = NULL; 261 262 _SPINUNLOCK(&(*cond)->lock); 263 } 264 265 /* 266 * Note that even though this thread may have 267 * been canceled, POSIX requires that the mutex 268 * be reaquired prior to cancellation. 269 */ 270 rval = _mutex_cv_lock(mutex); 271 } 272 } 273 break; 274 275 /* Trap invalid condition variable types: */ 276 default: 277 /* Unlock the condition variable structure: */ 278 _SPINUNLOCK(&(*cond)->lock); 279 280 /* Return an invalid argument error: */ 281 rval = EINVAL; 282 break; 283 } 284 285 if (interrupted != 0) { 286 if (_thread_run->continuation != NULL) 287 _thread_run->continuation((void *) _thread_run); 288 } 289 290 _thread_leave_cancellation_point(); 291 } 292 293 _thread_leave_cancellation_point(); 294 295 /* Return the completion status: */ 296 return (rval); 297} 298 299int 300pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 301 const struct timespec * abstime) 302{ 303 int rval = 0; 304 int interrupted = 0; 305 306 _thread_enter_cancellation_point(); 307 308 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 309 abstime->tv_nsec >= 1000000000) 310 rval = EINVAL; 311 /* 312 * If the condition variable is statically initialized, perform dynamic 313 * initialization. 314 */ 315 else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) { 316 _thread_enter_cancellation_point(); 317 318 /* Lock the condition variable structure: */ 319 _SPINLOCK(&(*cond)->lock); 320 321 /* 322 * If the condvar was statically allocated, properly 323 * initialize the tail queue. 324 */ 325 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 326 TAILQ_INIT(&(*cond)->c_queue); 327 (*cond)->c_flags |= COND_FLAGS_INITED; 328 } 329 330 /* Process according to condition variable type: */ 331 switch ((*cond)->c_type) { 332 /* Fast condition variable: */ 333 case COND_TYPE_FAST: 334 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 335 ((*cond)->c_mutex != *mutex))) { 336 /* Return invalid argument error: */ 337 rval = EINVAL; 338 339 /* Unlock the condition variable structure: */ 340 _SPINUNLOCK(&(*cond)->lock); 341 } else { 342 /* Set the wakeup time: */ 343 _thread_run->wakeup_time.tv_sec = 344 abstime->tv_sec; 345 _thread_run->wakeup_time.tv_nsec = 346 abstime->tv_nsec; 347 348 /* Reset the timeout and interrupted flags: */ 349 _thread_run->timeout = 0; 350 _thread_run->interrupted = 0; 351 352 /* 353 * Queue the running thread for the condition 354 * variable: 355 */ 356 cond_queue_enq(*cond, _thread_run); 357 358 /* Remember the mutex that is being used: */ 359 (*cond)->c_mutex = *mutex; 360 361 /* Unlock the mutex: */ 362 if ((rval = _mutex_cv_unlock(mutex)) != 0) { 363 /* 364 * Cannot unlock the mutex, so remove 365 * the running thread from the condition 366 * variable queue: 367 */ 368 cond_queue_remove(*cond, _thread_run); 369 370 /* Check for no more waiters: */ 371 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 372 (*cond)->c_mutex = NULL; 373 374 /* Unlock the condition variable structure: */ 375 _SPINUNLOCK(&(*cond)->lock); 376 } else { 377 /* 378 * Schedule the next thread and unlock 379 * the condition variable structure: 380 */ 381 _thread_kern_sched_state_unlock(PS_COND_WAIT, 382 &(*cond)->lock, __FILE__, __LINE__); 383 384 /* 385 * Check if the wait timedout or was 386 * interrupted (canceled): 387 */ 388 if ((_thread_run->timeout == 0) && 389 (_thread_run->interrupted == 0)) { 390 /* Lock the mutex: */ 391 rval = _mutex_cv_lock(mutex); 392 393 } else { 394 /* 395 * Remember if this thread was 396 * interrupted: 397 */ 398 interrupted = _thread_run->interrupted; 399 400 /* Lock the condition variable structure: */ 401 _SPINLOCK(&(*cond)->lock); 402 403 /* 404 * The wait timed out; remove 405 * the thread from the condition 406 * variable queue: 407 */ 408 cond_queue_remove(*cond, 409 _thread_run); 410 411 /* Check for no more waiters: */ 412 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 413 (*cond)->c_mutex = NULL; 414 415 /* Unock the condition variable structure: */ 416 _SPINUNLOCK(&(*cond)->lock); 417 418 /* Return a timeout error: */ 419 rval = ETIMEDOUT; 420 421 /* 422 * Lock the mutex and ignore any 423 * errors. Note that even though 424 * this thread may have been 425 * canceled, POSIX requires that 426 * the mutex be reaquired prior 427 * to cancellation. 428 */ 429 (void)_mutex_cv_lock(mutex); 430 } 431 } 432 } 433 break; 434 435 /* Trap invalid condition variable types: */ 436 default: 437 /* Unlock the condition variable structure: */ 438 _SPINUNLOCK(&(*cond)->lock); 439 440 /* Return an invalid argument error: */ 441 rval = EINVAL; 442 break; 443 } 444 445 if (interrupted != 0) { 446 if (_thread_run->continuation != NULL) 447 _thread_run->continuation((void *) _thread_run); 448 } 449 450 _thread_leave_cancellation_point(); 451 } 452 453 _thread_leave_cancellation_point(); 454 455 /* Return the completion status: */ 456 return (rval); 457} 458 459int 460pthread_cond_signal(pthread_cond_t * cond) 461{ 462 int rval = 0; 463 pthread_t pthread; 464 465 if (cond == NULL) 466 rval = EINVAL; 467 /* 468 * If the condition variable is statically initialized, perform dynamic 469 * initialization. 470 */ 471 else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL) == 0)) { 472 /* 473 * Defer signals to protect the scheduling queues 474 * from access by the signal handler: 475 */ 476 _thread_kern_sig_defer(); 477 478 /* Lock the condition variable structure: */ 479 _SPINLOCK(&(*cond)->lock); 480 481 /* Process according to condition variable type: */ 482 switch ((*cond)->c_type) { 483 /* Fast condition variable: */ 484 case COND_TYPE_FAST: 485 if ((pthread = cond_queue_deq(*cond)) != NULL) { 486 /* 487 * Unless the thread is currently suspended, 488 * allow it to run. If the thread is suspended, 489 * make a note that the thread isn't in a wait 490 * queue any more. 491 */ 492 if (pthread->state != PS_SUSPENDED) 493 PTHREAD_NEW_STATE(pthread,PS_RUNNING); 494 else 495 pthread->suspended = SUSP_NOWAIT; 496 } 497 498 /* Check for no more waiters: */ 499 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 500 (*cond)->c_mutex = NULL; 501 break; 502 503 /* Trap invalid condition variable types: */ 504 default: 505 /* Return an invalid argument error: */ 506 rval = EINVAL; 507 break; 508 } 509 510 /* Unlock the condition variable structure: */ 511 _SPINUNLOCK(&(*cond)->lock); 512 513 /* 514 * Undefer and handle pending signals, yielding if 515 * necessary: 516 */ 517 _thread_kern_sig_undefer(); 518 } 519 520 /* Return the completion status: */ 521 return (rval); 522} 523 524int 525pthread_cond_broadcast(pthread_cond_t * cond) 526{ 527 int rval = 0; 528 pthread_t pthread; 529 530 if (cond == NULL) 531 rval = EINVAL; 532 /* 533 * If the condition variable is statically initialized, perform dynamic 534 * initialization. 535 */ 536 else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL) == 0)) { 537 /* 538 * Defer signals to protect the scheduling queues 539 * from access by the signal handler: 540 */ 541 _thread_kern_sig_defer(); 542 543 /* Lock the condition variable structure: */ 544 _SPINLOCK(&(*cond)->lock); 545 546 /* Process according to condition variable type: */ 547 switch ((*cond)->c_type) { 548 /* Fast condition variable: */ 549 case COND_TYPE_FAST: 550 /* 551 * Enter a loop to bring all threads off the 552 * condition queue: 553 */ 554 while ((pthread = cond_queue_deq(*cond)) != NULL) { 555 /* 556 * Unless the thread is currently suspended, 557 * allow it to run. If the thread is suspended, 558 * make a note that the thread isn't in a wait 559 * queue any more. 560 */ 561 if (pthread->state != PS_SUSPENDED) 562 PTHREAD_NEW_STATE(pthread,PS_RUNNING); 563 else 564 pthread->suspended = SUSP_NOWAIT; 565 } 566 567 /* There are no more waiting threads: */ 568 (*cond)->c_mutex = NULL; 569 break; 570 571 /* Trap invalid condition variable types: */ 572 default: 573 /* Return an invalid argument error: */ 574 rval = EINVAL; 575 break; 576 } 577 578 /* Unlock the condition variable structure: */ 579 _SPINUNLOCK(&(*cond)->lock); 580 581 /* 582 * Undefer and handle pending signals, yielding if 583 * necessary: 584 */ 585 _thread_kern_sig_undefer(); 586 } 587 588 /* Return the completion status: */ 589 return (rval); 590} 591 592/* 593 * Dequeue a waiting thread from the head of a condition queue in 594 * descending priority order. 595 */ 596static inline pthread_t 597cond_queue_deq(pthread_cond_t cond) 598{ 599 pthread_t pthread; 600 601 while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) { 602 TAILQ_REMOVE(&cond->c_queue, pthread, qe); 603 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ; 604 if ((pthread->timeout == 0) && (pthread->interrupted == 0)) 605 /* 606 * Only exit the loop when we find a thread 607 * that hasn't timed out or been canceled; 608 * those threads are already running and don't 609 * need their run state changed. 610 */ 611 break; 612 } 613 614 return(pthread); 615} 616 617/* 618 * Remove a waiting thread from a condition queue in descending priority 619 * order. 620 */ 621static inline void 622cond_queue_remove(pthread_cond_t cond, pthread_t pthread) 623{ 624 /* 625 * Because pthread_cond_timedwait() can timeout as well 626 * as be signaled by another thread, it is necessary to 627 * guard against removing the thread from the queue if 628 * it isn't in the queue. 629 */ 630 if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) { 631 TAILQ_REMOVE(&cond->c_queue, pthread, qe); 632 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ; 633 } 634} 635 636/* 637 * Enqueue a waiting thread to a condition queue in descending priority 638 * order. 639 */ 640static inline void 641cond_queue_enq(pthread_cond_t cond, pthread_t pthread) 642{ 643 pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head); 644 645 /* 646 * For the common case of all threads having equal priority, 647 * we perform a quick check against the priority of the thread 648 * at the tail of the queue. 649 */ 650 if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) 651 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe); 652 else { 653 tid = TAILQ_FIRST(&cond->c_queue); 654 while (pthread->active_priority <= tid->active_priority) 655 tid = TAILQ_NEXT(tid, qe); 656 TAILQ_INSERT_BEFORE(tid, pthread, qe); 657 } 658 pthread->flags |= PTHREAD_FLAGS_IN_CONDQ; 659} 660#endif 661