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