thr_cond.c revision 50601
10Sstevel@tonic-gate/* 20Sstevel@tonic-gate * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. 30Sstevel@tonic-gate * All rights reserved. 40Sstevel@tonic-gate * 5420Sstevel * Redistribution and use in source and binary forms, with or without 6420Sstevel * modification, are permitted provided that the following conditions 70Sstevel@tonic-gate * are met: 80Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 90Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 100Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 110Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 120Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 130Sstevel@tonic-gate * 3. All advertising materials mentioning features or use of this software 140Sstevel@tonic-gate * must display the following acknowledgement: 150Sstevel@tonic-gate * This product includes software developed by John Birrell. 160Sstevel@tonic-gate * 4. Neither the name of the author nor the names of any co-contributors 170Sstevel@tonic-gate * may be used to endorse or promote products derived from this software 180Sstevel@tonic-gate * without specific prior written permission. 190Sstevel@tonic-gate * 2059Sjbeck * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 2159Sjbeck * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2259Sjbeck * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2312041SJohn.Beck@Sun.COM * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 240Sstevel@tonic-gate * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 250Sstevel@tonic-gate * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 260Sstevel@tonic-gate * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 270Sstevel@tonic-gate * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 280Sstevel@tonic-gate * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 290Sstevel@tonic-gate * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 300Sstevel@tonic-gate * SUCH DAMAGE. 310Sstevel@tonic-gate * 320Sstevel@tonic-gate * $FreeBSD: head/lib/libkse/thread/thr_cond.c 50601 1999-08-30 00:02:08Z deischen $ 330Sstevel@tonic-gate */ 340Sstevel@tonic-gate#include <stdlib.h> 350Sstevel@tonic-gate#include <errno.h> 360Sstevel@tonic-gate#include <string.h> 370Sstevel@tonic-gate#ifdef _THREAD_SAFE 380Sstevel@tonic-gate#include <pthread.h> 390Sstevel@tonic-gate#include "pthread_private.h" 400Sstevel@tonic-gate 410Sstevel@tonic-gate/* 420Sstevel@tonic-gate * Prototypes 430Sstevel@tonic-gate */ 440Sstevel@tonic-gatestatic inline pthread_t cond_queue_deq(pthread_cond_t); 450Sstevel@tonic-gatestatic inline void cond_queue_remove(pthread_cond_t, pthread_t); 460Sstevel@tonic-gatestatic inline void cond_queue_enq(pthread_cond_t, pthread_t); 470Sstevel@tonic-gate 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 status; 162 163 if (cond == NULL) 164 rval = EINVAL; 165 166 /* 167 * If the condition variable is statically initialized, 168 * perform the dynamic initialization: 169 */ 170 else if (*cond != NULL || 171 (rval = pthread_cond_init(cond,NULL)) == 0) { 172 /* Lock the condition variable structure: */ 173 _SPINLOCK(&(*cond)->lock); 174 175 /* 176 * If the condvar was statically allocated, properly 177 * initialize the tail queue. 178 */ 179 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 180 TAILQ_INIT(&(*cond)->c_queue); 181 (*cond)->c_flags |= COND_FLAGS_INITED; 182 } 183 184 /* Process according to condition variable type: */ 185 switch ((*cond)->c_type) { 186 /* Fast condition variable: */ 187 case COND_TYPE_FAST: 188 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 189 ((*cond)->c_mutex != *mutex))) { 190 /* Unlock the condition variable structure: */ 191 _SPINUNLOCK(&(*cond)->lock); 192 193 /* Return invalid argument error: */ 194 rval = EINVAL; 195 } else { 196 /* Reset the timeout flag: */ 197 _thread_run->timeout = 0; 198 199 /* 200 * Queue the running thread for the condition 201 * variable: 202 */ 203 cond_queue_enq(*cond, _thread_run); 204 205 /* Remember the mutex that is being used: */ 206 (*cond)->c_mutex = *mutex; 207 208 /* Wait forever: */ 209 _thread_run->wakeup_time.tv_sec = -1; 210 211 /* Unlock the mutex: */ 212 if ((rval = _mutex_cv_unlock(mutex)) != 0) { 213 /* 214 * Cannot unlock the mutex, so remove 215 * the running thread from the condition 216 * variable queue: 217 */ 218 cond_queue_remove(*cond, _thread_run); 219 220 /* Check for no more waiters: */ 221 if (TAILQ_FIRST(&(*cond)->c_queue) == 222 NULL) 223 (*cond)->c_mutex = NULL; 224 225 /* Unlock the condition variable structure: */ 226 _SPINUNLOCK(&(*cond)->lock); 227 } 228 else { 229 /* 230 * Schedule the next thread and unlock 231 * the condition variable structure: 232 */ 233 _thread_kern_sched_state_unlock(PS_COND_WAIT, 234 &(*cond)->lock, __FILE__, __LINE__); 235 236 /* Lock the mutex: */ 237 rval = _mutex_cv_lock(mutex); 238 } 239 } 240 break; 241 242 /* Trap invalid condition variable types: */ 243 default: 244 /* Unlock the condition variable structure: */ 245 _SPINUNLOCK(&(*cond)->lock); 246 247 /* Return an invalid argument error: */ 248 rval = EINVAL; 249 break; 250 } 251 } 252 253 /* Return the completion status: */ 254 return (rval); 255} 256 257int 258pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 259 const struct timespec * abstime) 260{ 261 int rval = 0; 262 int status; 263 264 if (cond == NULL || abstime == NULL) 265 rval = EINVAL; 266 267 if (abstime->tv_sec < 0 || 268 abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { 269 errno = EINVAL; 270 return (-1); 271 } 272 273 /* 274 * If the condition variable is statically initialized, 275 * perform the dynamic initialization: 276 */ 277 if (*cond != NULL || 278 (rval = pthread_cond_init(cond,NULL)) == 0) { 279 /* Lock the condition variable structure: */ 280 _SPINLOCK(&(*cond)->lock); 281 282 /* 283 * If the condvar was statically allocated, properly 284 * initialize the tail queue. 285 */ 286 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 287 TAILQ_INIT(&(*cond)->c_queue); 288 (*cond)->c_flags |= COND_FLAGS_INITED; 289 } 290 291 /* Process according to condition variable type: */ 292 switch ((*cond)->c_type) { 293 /* Fast condition variable: */ 294 case COND_TYPE_FAST: 295 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 296 ((*cond)->c_mutex != *mutex))) { 297 /* Return invalid argument error: */ 298 rval = EINVAL; 299 300 /* Unlock the condition variable structure: */ 301 _SPINUNLOCK(&(*cond)->lock); 302 } else { 303 /* Set the wakeup time: */ 304 _thread_run->wakeup_time.tv_sec = 305 abstime->tv_sec; 306 _thread_run->wakeup_time.tv_nsec = 307 abstime->tv_nsec; 308 309 /* Reset the timeout flag: */ 310 _thread_run->timeout = 0; 311 312 /* 313 * Queue the running thread for the condition 314 * variable: 315 */ 316 cond_queue_enq(*cond, _thread_run); 317 318 /* Remember the mutex that is being used: */ 319 (*cond)->c_mutex = *mutex; 320 321 /* Unlock the mutex: */ 322 if ((rval = _mutex_cv_unlock(mutex)) != 0) { 323 /* 324 * Cannot unlock the mutex, so remove 325 * the running thread from the condition 326 * variable queue: 327 */ 328 cond_queue_remove(*cond, _thread_run); 329 330 /* Check for no more waiters: */ 331 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 332 (*cond)->c_mutex = NULL; 333 334 /* Unlock the condition variable structure: */ 335 _SPINUNLOCK(&(*cond)->lock); 336 } else { 337 /* 338 * Schedule the next thread and unlock 339 * the condition variable structure: 340 */ 341 _thread_kern_sched_state_unlock(PS_COND_WAIT, 342 &(*cond)->lock, __FILE__, __LINE__); 343 344 /* Check if the wait timedout: */ 345 if (_thread_run->timeout == 0) { 346 /* Lock the mutex: */ 347 rval = _mutex_cv_lock(mutex); 348 } 349 else { 350 /* Lock the condition variable structure: */ 351 _SPINLOCK(&(*cond)->lock); 352 353 /* 354 * The wait timed out; remove 355 * the thread from the condition 356 * variable queue: 357 */ 358 cond_queue_remove(*cond, 359 _thread_run); 360 361 /* Check for no more waiters: */ 362 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 363 (*cond)->c_mutex = NULL; 364 365 /* Unock the condition variable structure: */ 366 _SPINUNLOCK(&(*cond)->lock); 367 368 /* Return a timeout error: */ 369 rval = ETIMEDOUT; 370 371 /* 372 * Lock the mutex and ignore 373 * any errors: 374 */ 375 (void)_mutex_cv_lock(mutex); 376 } 377 } 378 } 379 break; 380 381 /* Trap invalid condition variable types: */ 382 default: 383 /* Unlock the condition variable structure: */ 384 _SPINUNLOCK(&(*cond)->lock); 385 386 /* Return an invalid argument error: */ 387 rval = EINVAL; 388 break; 389 } 390 391 } 392 393 /* Return the completion status: */ 394 return (rval); 395} 396 397int 398pthread_cond_signal(pthread_cond_t * cond) 399{ 400 int rval = 0; 401 pthread_t pthread; 402 403 if (cond == NULL || *cond == NULL) 404 rval = EINVAL; 405 else { 406 /* 407 * Defer signals to protect the scheduling queues 408 * from access by the signal handler: 409 */ 410 _thread_kern_sig_defer(); 411 412 /* Lock the condition variable structure: */ 413 _SPINLOCK(&(*cond)->lock); 414 415 /* Process according to condition variable type: */ 416 switch ((*cond)->c_type) { 417 /* Fast condition variable: */ 418 case COND_TYPE_FAST: 419 /* 420 * Enter a loop to dequeue threads from the condition 421 * queue until we find one that hasn't previously 422 * timed out. 423 */ 424 while (((pthread = cond_queue_deq(*cond)) != NULL) && 425 (pthread->timeout != 0)) { 426 } 427 428 if (pthread != NULL) 429 /* Allow the thread to run: */ 430 PTHREAD_NEW_STATE(pthread,PS_RUNNING); 431 432 /* Check for no more waiters: */ 433 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL) 434 (*cond)->c_mutex = NULL; 435 break; 436 437 /* Trap invalid condition variable types: */ 438 default: 439 /* Return an invalid argument error: */ 440 rval = EINVAL; 441 break; 442 } 443 444 /* Unlock the condition variable structure: */ 445 _SPINUNLOCK(&(*cond)->lock); 446 447 /* 448 * Undefer and handle pending signals, yielding if 449 * necessary: 450 */ 451 _thread_kern_sig_undefer(); 452 } 453 454 /* Return the completion status: */ 455 return (rval); 456} 457 458int 459pthread_cond_broadcast(pthread_cond_t * cond) 460{ 461 int rval = 0; 462 pthread_t pthread; 463 464 if (cond == NULL || *cond == NULL) 465 rval = EINVAL; 466 else { 467 /* 468 * Defer signals to protect the scheduling queues 469 * from access by the signal handler: 470 */ 471 _thread_kern_sig_defer(); 472 473 /* Lock the condition variable structure: */ 474 _SPINLOCK(&(*cond)->lock); 475 476 /* Process according to condition variable type: */ 477 switch ((*cond)->c_type) { 478 /* Fast condition variable: */ 479 case COND_TYPE_FAST: 480 /* 481 * Enter a loop to bring all threads off the 482 * condition queue: 483 */ 484 while ((pthread = cond_queue_deq(*cond)) != NULL) { 485 /* 486 * The thread is already running if the 487 * timeout flag is set. 488 */ 489 if (pthread->timeout == 0) 490 PTHREAD_NEW_STATE(pthread,PS_RUNNING); 491 } 492 493 /* There are no more waiting threads: */ 494 (*cond)->c_mutex = NULL; 495 break; 496 497 /* Trap invalid condition variable types: */ 498 default: 499 /* Return an invalid argument error: */ 500 rval = EINVAL; 501 break; 502 } 503 504 /* Unlock the condition variable structure: */ 505 _SPINUNLOCK(&(*cond)->lock); 506 507 /* 508 * Undefer and handle pending signals, yielding if 509 * necessary: 510 */ 511 _thread_kern_sig_undefer(); 512 } 513 514 /* Return the completion status: */ 515 return (rval); 516} 517 518/* 519 * Dequeue a waiting thread from the head of a condition queue in 520 * descending priority order. 521 */ 522static inline pthread_t 523cond_queue_deq(pthread_cond_t cond) 524{ 525 pthread_t pthread; 526 527 if ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) { 528 TAILQ_REMOVE(&cond->c_queue, pthread, qe); 529 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ; 530 } 531 532 return(pthread); 533} 534 535/* 536 * Remove a waiting thread from a condition queue in descending priority 537 * order. 538 */ 539static inline void 540cond_queue_remove(pthread_cond_t cond, pthread_t pthread) 541{ 542 /* 543 * Because pthread_cond_timedwait() can timeout as well 544 * as be signaled by another thread, it is necessary to 545 * guard against removing the thread from the queue if 546 * it isn't in the queue. 547 */ 548 if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) { 549 TAILQ_REMOVE(&cond->c_queue, pthread, qe); 550 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ; 551 } 552} 553 554/* 555 * Enqueue a waiting thread to a condition queue in descending priority 556 * order. 557 */ 558static inline void 559cond_queue_enq(pthread_cond_t cond, pthread_t pthread) 560{ 561 pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head); 562 563 /* 564 * For the common case of all threads having equal priority, 565 * we perform a quick check against the priority of the thread 566 * at the tail of the queue. 567 */ 568 if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) 569 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe); 570 else { 571 tid = TAILQ_FIRST(&cond->c_queue); 572 while (pthread->active_priority <= tid->active_priority) 573 tid = TAILQ_NEXT(tid, qe); 574 TAILQ_INSERT_BEFORE(tid, pthread, qe); 575 } 576 pthread->flags |= PTHREAD_FLAGS_IN_CONDQ; 577} 578#endif 579