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