thr_cond.c revision 61681
1239310Sdim/* 2239310Sdim * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. 3239310Sdim * All rights reserved. 4239310Sdim * 5239310Sdim * Redistribution and use in source and binary forms, with or without 6239310Sdim * modification, are permitted provided that the following conditions 7239310Sdim * are met: 8239310Sdim * 1. Redistributions of source code must retain the above copyright 9239310Sdim * notice, this list of conditions and the following disclaimer. 10239310Sdim * 2. Redistributions in binary form must reproduce the above copyright 11239310Sdim * notice, this list of conditions and the following disclaimer in the 12239310Sdim * documentation and/or other materials provided with the distribution. 13239310Sdim * 3. All advertising materials mentioning features or use of this software 14239310Sdim * must display the following acknowledgement: 15249423Sdim * This product includes software developed by John Birrell. 16239310Sdim * 4. Neither the name of the author nor the names of any co-contributors 17249423Sdim * may be used to endorse or promote products derived from this software 18249423Sdim * without specific prior written permission. 19239310Sdim * 20249423Sdim * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 21239310Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22239310Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23239310Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24239310Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25239310Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26239310Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27239310Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28249423Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29239310Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30239310Sdim * SUCH DAMAGE. 31239310Sdim * 32239310Sdim * $FreeBSD: head/lib/libkse/thread/thr_cond.c 61681 2000-06-14 17:17:41Z jasone $ 33249423Sdim */ 34249423Sdim#include <stdlib.h> 35249423Sdim#include <errno.h> 36249423Sdim#include <string.h> 37239310Sdim#ifdef _THREAD_SAFE 38239310Sdim#include <pthread.h> 39239310Sdim#include "pthread_private.h" 40239310Sdim 41239310Sdim/* 42239310Sdim * Prototypes 43239310Sdim */ 44239310Sdimstatic inline pthread_t cond_queue_deq(pthread_cond_t); 45239310Sdimstatic inline void cond_queue_remove(pthread_cond_t, pthread_t); 46239310Sdimstatic inline void cond_queue_enq(pthread_cond_t, pthread_t); 47239310Sdim 48239310Sdim/* Reinitialize a condition variable to defaults. */ 49239310Sdimint 50249423Sdim_cond_reinit(pthread_cond_t * cond) 51249423Sdim{ 52251662Sdim int ret = 0; 53249423Sdim 54239310Sdim if (cond == NULL) 55239310Sdim ret = EINVAL; 56239310Sdim else if (*cond == NULL) 57239310Sdim ret = pthread_cond_init(cond, NULL); 58239310Sdim else { 59239310Sdim /* 60249423Sdim * Initialize the condition variable structure: 61249423Sdim */ 62249423Sdim TAILQ_INIT(&(*cond)->c_queue); 63251662Sdim (*cond)->c_flags = COND_FLAGS_INITED; 64239310Sdim (*cond)->c_type = COND_TYPE_FAST; 65239310Sdim (*cond)->c_mutex = NULL; 66249423Sdim memset(&(*cond)->lock, 0, sizeof((*cond)->lock)); 67249423Sdim } 68249423Sdim return (ret); 69249423Sdim} 70249423Sdim 71249423Sdimint 72249423Sdimpthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr) 73249423Sdim{ 74263508Sdim enum pthread_cond_type type; 75263508Sdim pthread_cond_t pcond; 76263508Sdim int rval = 0; 77239310Sdim 78239310Sdim if (cond == NULL) 79239310Sdim rval = EINVAL; 80249423Sdim else { 81249423Sdim /* 82249423Sdim * Check if a pointer to a condition variable attribute 83249423Sdim * structure was passed by the caller: 84249423Sdim */ 85239310Sdim if (cond_attr != NULL && *cond_attr != NULL) { 86239310Sdim /* Default to a fast condition variable: */ 87239310Sdim type = (*cond_attr)->c_type; 88249423Sdim } else { 89249423Sdim /* Default to a fast condition variable: */ 90249423Sdim type = COND_TYPE_FAST; 91249423Sdim } 92249423Sdim 93239310Sdim /* Process according to condition variable type: */ 94263508Sdim switch (type) { 95239310Sdim /* Fast condition variable: */ 96239310Sdim case COND_TYPE_FAST: 97239310Sdim /* Nothing to do here. */ 98249423Sdim break; 99239310Sdim 100239310Sdim /* Trap invalid condition variable types: */ 101239310Sdim default: 102239310Sdim /* Return an invalid argument error: */ 103239310Sdim rval = EINVAL; 104251662Sdim break; 105239310Sdim } 106239310Sdim 107263508Sdim /* Check for no errors: */ 108263508Sdim if (rval == 0) { 109263508Sdim if ((pcond = (pthread_cond_t) 110263508Sdim malloc(sizeof(struct pthread_cond))) == NULL) { 111263508Sdim rval = ENOMEM; 112239310Sdim } else { 113263508Sdim /* 114239310Sdim * Initialise the condition variable 115239310Sdim * structure: 116239310Sdim */ 117239310Sdim TAILQ_INIT(&pcond->c_queue); 118239310Sdim pcond->c_flags |= COND_FLAGS_INITED; 119239310Sdim pcond->c_type = type; 120251662Sdim pcond->c_mutex = NULL; 121263508Sdim memset(&pcond->lock,0,sizeof(pcond->lock)); 122263508Sdim *cond = pcond; 123263508Sdim } 124263508Sdim } 125263508Sdim } 126263508Sdim /* Return the completion status: */ 127263508Sdim return (rval); 128263508Sdim} 129263508Sdim 130263508Sdimint 131251662Sdimpthread_cond_destroy(pthread_cond_t * cond) 132251662Sdim{ 133251662Sdim int rval = 0; 134251662Sdim 135239310Sdim if (cond == NULL || *cond == NULL) 136239310Sdim rval = EINVAL; 137239310Sdim else { 138239310Sdim /* Lock the condition variable structure: */ 139239310Sdim _SPINLOCK(&(*cond)->lock); 140239310Sdim 141239310Sdim /* 142239310Sdim * Free the memory allocated for the condition 143249423Sdim * variable structure: 144263508Sdim */ 145263508Sdim free(*cond); 146263508Sdim 147263508Sdim /* 148263508Sdim * NULL the caller's pointer now that the condition 149263508Sdim * variable has been destroyed: 150263508Sdim */ 151263508Sdim *cond = NULL; 152263508Sdim } 153263508Sdim /* Return the completion status: */ 154263508Sdim return (rval); 155263508Sdim} 156263508Sdim 157263508Sdimint 158263508Sdimpthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) 159263508Sdim{ 160263508Sdim int rval = 0; 161263508Sdim int interrupted = 0; 162263508Sdim 163263508Sdim _thread_enter_cancellation_point(); 164263508Sdim 165263508Sdim if (cond == NULL) 166263508Sdim rval = EINVAL; 167263508Sdim 168263508Sdim /* 169263508Sdim * If the condition variable is statically initialized, 170263508Sdim * perform the dynamic initialization: 171263508Sdim */ 172263508Sdim else if (*cond != NULL || 173263508Sdim (rval = pthread_cond_init(cond,NULL)) == 0) { 174263508Sdim 175263508Sdim _thread_enter_cancellation_point(); 176263508Sdim 177263508Sdim /* Lock the condition variable structure: */ 178263508Sdim _SPINLOCK(&(*cond)->lock); 179263508Sdim 180263508Sdim /* 181263508Sdim * 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 (cond == NULL || abstime == NULL) 309 rval = EINVAL; 310 311 if (abstime->tv_sec < 0 || 312 abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { 313 errno = EINVAL; 314 _thread_leave_cancellation_point(); 315 return (-1); 316 } 317 318 /* 319 * If the condition variable is statically initialized, 320 * perform the dynamic initialization: 321 */ 322 if (*cond != NULL || 323 (rval = pthread_cond_init(cond,NULL)) == 0) { 324 325 _thread_enter_cancellation_point(); 326 327 /* Lock the condition variable structure: */ 328 _SPINLOCK(&(*cond)->lock); 329 330 /* 331 * If the condvar was statically allocated, properly 332 * initialize the tail queue. 333 */ 334 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 335 TAILQ_INIT(&(*cond)->c_queue); 336 (*cond)->c_flags |= COND_FLAGS_INITED; 337 } 338 339 /* Process according to condition variable type: */ 340 switch ((*cond)->c_type) { 341 /* Fast condition variable: */ 342 case COND_TYPE_FAST: 343 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 344 ((*cond)->c_mutex != *mutex))) { 345 /* Return invalid argument error: */ 346 rval = EINVAL; 347 348 /* Unlock the condition variable structure: */ 349 _SPINUNLOCK(&(*cond)->lock); 350 } else { 351 /* Set the wakeup time: */ 352 _thread_run->wakeup_time.tv_sec = 353 abstime->tv_sec; 354 _thread_run->wakeup_time.tv_nsec = 355 abstime->tv_nsec; 356 357 /* Reset the timeout and interrupted flags: */ 358 _thread_run->timeout = 0; 359 _thread_run->interrupted = 0; 360 361 /* 362 * Queue the running thread for the condition 363 * variable: 364 */ 365 cond_queue_enq(*cond, _thread_run); 366 367 /* Remember the mutex that is being used: */ 368 (*cond)->c_mutex = *mutex; 369 370 /* Unlock the mutex: */ 371 if ((rval = _mutex_cv_unlock(mutex)) != 0) { 372 /* 373 * Cannot unlock the mutex, so remove 374 * the running thread from the condition 375 * variable queue: 376 */ 377 cond_queue_remove(*cond, _thread_run); 378 379 /* Check for no more waiters: */ 380 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 381 (*cond)->c_mutex = NULL; 382 383 /* Unlock the condition variable structure: */ 384 _SPINUNLOCK(&(*cond)->lock); 385 } else { 386 /* 387 * Schedule the next thread and unlock 388 * the condition variable structure: 389 */ 390 _thread_kern_sched_state_unlock(PS_COND_WAIT, 391 &(*cond)->lock, __FILE__, __LINE__); 392 393 /* 394 * Check if the wait timedout or was 395 * interrupted (canceled): 396 */ 397 if ((_thread_run->timeout == 0) && 398 (_thread_run->interrupted == 0)) { 399 /* Lock the mutex: */ 400 rval = _mutex_cv_lock(mutex); 401 402 } else { 403 /* 404 * Remember if this thread was 405 * interrupted: 406 */ 407 interrupted = _thread_run->interrupted; 408 409 /* Lock the condition variable structure: */ 410 _SPINLOCK(&(*cond)->lock); 411 412 /* 413 * The wait timed out; remove 414 * the thread from the condition 415 * variable queue: 416 */ 417 cond_queue_remove(*cond, 418 _thread_run); 419 420 /* Check for no more waiters: */ 421 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 422 (*cond)->c_mutex = NULL; 423 424 /* Unock the condition variable structure: */ 425 _SPINUNLOCK(&(*cond)->lock); 426 427 /* Return a timeout error: */ 428 rval = ETIMEDOUT; 429 430 /* 431 * Lock the mutex and ignore any 432 * errors. Note that even though 433 * this thread may have been 434 * canceled, POSIX requires that 435 * the mutex be reaquired prior 436 * to cancellation. 437 */ 438 (void)_mutex_cv_lock(mutex); 439 } 440 } 441 } 442 break; 443 444 /* Trap invalid condition variable types: */ 445 default: 446 /* Unlock the condition variable structure: */ 447 _SPINUNLOCK(&(*cond)->lock); 448 449 /* Return an invalid argument error: */ 450 rval = EINVAL; 451 break; 452 } 453 454 if (interrupted != 0) { 455 if (_thread_run->continuation != NULL) 456 _thread_run->continuation((void *) _thread_run); 457 } 458 459 _thread_leave_cancellation_point(); 460 } 461 462 _thread_leave_cancellation_point(); 463 464 /* Return the completion status: */ 465 return (rval); 466} 467 468int 469pthread_cond_signal(pthread_cond_t * cond) 470{ 471 int rval = 0; 472 pthread_t pthread; 473 474 if (cond == NULL || *cond == NULL) 475 rval = EINVAL; 476 else { 477 /* 478 * Defer signals to protect the scheduling queues 479 * from access by the signal handler: 480 */ 481 _thread_kern_sig_defer(); 482 483 /* Lock the condition variable structure: */ 484 _SPINLOCK(&(*cond)->lock); 485 486 /* Process according to condition variable type: */ 487 switch ((*cond)->c_type) { 488 /* Fast condition variable: */ 489 case COND_TYPE_FAST: 490 if ((pthread = cond_queue_deq(*cond)) != NULL) { 491 /* 492 * Unless the thread is currently suspended, 493 * allow it to run. If the thread is suspended, 494 * make a note that the thread isn't in a wait 495 * queue any more. 496 */ 497 if (pthread->state != PS_SUSPENDED) 498 PTHREAD_NEW_STATE(pthread,PS_RUNNING); 499 else 500 pthread->suspended = SUSP_NOWAIT; 501 } 502 503 /* Check for no more waiters: */ 504 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 505 (*cond)->c_mutex = NULL; 506 break; 507 508 /* Trap invalid condition variable types: */ 509 default: 510 /* Return an invalid argument error: */ 511 rval = EINVAL; 512 break; 513 } 514 515 /* Unlock the condition variable structure: */ 516 _SPINUNLOCK(&(*cond)->lock); 517 518 /* 519 * Undefer and handle pending signals, yielding if 520 * necessary: 521 */ 522 _thread_kern_sig_undefer(); 523 } 524 525 /* Return the completion status: */ 526 return (rval); 527} 528 529int 530pthread_cond_broadcast(pthread_cond_t * cond) 531{ 532 int rval = 0; 533 pthread_t pthread; 534 535 if (cond == NULL || *cond == NULL) 536 rval = EINVAL; 537 else { 538 /* 539 * Defer signals to protect the scheduling queues 540 * from access by the signal handler: 541 */ 542 _thread_kern_sig_defer(); 543 544 /* Lock the condition variable structure: */ 545 _SPINLOCK(&(*cond)->lock); 546 547 /* Process according to condition variable type: */ 548 switch ((*cond)->c_type) { 549 /* Fast condition variable: */ 550 case COND_TYPE_FAST: 551 /* 552 * Enter a loop to bring all threads off the 553 * condition queue: 554 */ 555 while ((pthread = cond_queue_deq(*cond)) != NULL) { 556 /* 557 * Unless the thread is currently suspended, 558 * allow it to run. If the thread is suspended, 559 * make a note that the thread isn't in a wait 560 * queue any more. 561 */ 562 if (pthread->state != PS_SUSPENDED) 563 PTHREAD_NEW_STATE(pthread,PS_RUNNING); 564 else 565 pthread->suspended = SUSP_NOWAIT; 566 } 567 568 /* There are no more waiting threads: */ 569 (*cond)->c_mutex = NULL; 570 break; 571 572 /* Trap invalid condition variable types: */ 573 default: 574 /* Return an invalid argument error: */ 575 rval = EINVAL; 576 break; 577 } 578 579 /* Unlock the condition variable structure: */ 580 _SPINUNLOCK(&(*cond)->lock); 581 582 /* 583 * Undefer and handle pending signals, yielding if 584 * necessary: 585 */ 586 _thread_kern_sig_undefer(); 587 } 588 589 /* Return the completion status: */ 590 return (rval); 591} 592 593/* 594 * Dequeue a waiting thread from the head of a condition queue in 595 * descending priority order. 596 */ 597static inline pthread_t 598cond_queue_deq(pthread_cond_t cond) 599{ 600 pthread_t pthread; 601 602 while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) { 603 TAILQ_REMOVE(&cond->c_queue, pthread, qe); 604 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ; 605 if ((pthread->timeout == 0) && (pthread->interrupted == 0)) 606 /* 607 * Only exit the loop when we find a thread 608 * that hasn't timed out or been canceled; 609 * those threads are already running and don't 610 * need their run state changed. 611 */ 612 break; 613 } 614 615 return(pthread); 616} 617 618/* 619 * Remove a waiting thread from a condition queue in descending priority 620 * order. 621 */ 622static inline void 623cond_queue_remove(pthread_cond_t cond, pthread_t pthread) 624{ 625 /* 626 * Because pthread_cond_timedwait() can timeout as well 627 * as be signaled by another thread, it is necessary to 628 * guard against removing the thread from the queue if 629 * it isn't in the queue. 630 */ 631 if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) { 632 TAILQ_REMOVE(&cond->c_queue, pthread, qe); 633 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ; 634 } 635} 636 637/* 638 * Enqueue a waiting thread to a condition queue in descending priority 639 * order. 640 */ 641static inline void 642cond_queue_enq(pthread_cond_t cond, pthread_t pthread) 643{ 644 pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head); 645 646 /* 647 * For the common case of all threads having equal priority, 648 * we perform a quick check against the priority of the thread 649 * at the tail of the queue. 650 */ 651 if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) 652 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe); 653 else { 654 tid = TAILQ_FIRST(&cond->c_queue); 655 while (pthread->active_priority <= tid->active_priority) 656 tid = TAILQ_NEXT(tid, qe); 657 TAILQ_INSERT_BEFORE(tid, pthread, qe); 658 } 659 pthread->flags |= PTHREAD_FLAGS_IN_CONDQ; 660} 661#endif 662