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 * Proctect two different threads calling a pthread_cond_* function 42 * from accidentally initializing the condition variable twice. 43 */ 44 static spinlock_t static_cond_lock = _SPINLOCK_INITIALIZER; 45 46 /* 47 * Prototypes 48 */ 49 static inline int cond_init(pthread_cond_t *); 50 static pthread_t cond_queue_deq(pthread_cond_t); 51 static void cond_queue_remove(pthread_cond_t, pthread_t); 52 static void cond_queue_enq(pthread_cond_t, pthread_t); 53 static int cond_signal(pthread_cond_t *, int); 54 static int cond_wait_common(pthread_cond_t *, 55 pthread_mutex_t *, const struct timespec *); 56 57 __weak_reference(_pthread_cond_init, pthread_cond_init); 58 __weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 59 __weak_reference(_pthread_cond_wait, pthread_cond_wait); 60 __weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait); 61 __weak_reference(_pthread_cond_signal, pthread_cond_signal); 62 __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 63 64 #define COND_LOCK(c) \ 65 do { \ 66 if (umtx_lock(&(c)->c_lock, curthread->thr_id)) \ 67 abort(); \ 68 } while (0) 69 70 #define COND_UNLOCK(c) \ 71 do { \ 72 if (umtx_unlock(&(c)->c_lock, curthread->thr_id)) \ 73 abort(); \ 74 } while (0) 75 76 77 /* Reinitialize a condition variable to defaults. */ 78 int 79 _cond_reinit(pthread_cond_t *cond) 80 { 81 if (cond == NULL) 82 return (EINVAL); 83 84 if (*cond == NULL) 85 return (pthread_cond_init(cond, NULL)); 86 87 /* 88 * Initialize the condition variable structure: 89 */ 90 TAILQ_INIT(&(*cond)->c_queue); 91 (*cond)->c_flags = COND_FLAGS_INITED; 92 (*cond)->c_type = COND_TYPE_FAST; 93 (*cond)->c_mutex = NULL; 94 (*cond)->c_seqno = 0; 95 bzero(&(*cond)->c_lock, sizeof((*cond)->c_lock)); 96 97 return (0); 98 } 99 100 int 101 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 102 { 103 enum pthread_cond_type type; 104 pthread_cond_t pcond; 105 106 if (cond == NULL) 107 return (EINVAL); 108 109 /* 110 * Check if a pointer to a condition variable attribute 111 * structure was passed by the caller: 112 */ 113 if (cond_attr != NULL && *cond_attr != NULL) 114 type = (*cond_attr)->c_type; 115 else 116 /* Default to a fast condition variable: */ 117 type = COND_TYPE_FAST; 118 119 /* Process according to condition variable type: */ 120 switch (type) { 121 case COND_TYPE_FAST: 122 break; 123 default: 124 return (EINVAL); 125 break; 126 } 127 128 if ((pcond = (pthread_cond_t) 129 malloc(sizeof(struct pthread_cond))) == NULL) 130 return (ENOMEM); 131 /* 132 * Initialise the condition variable 133 * structure: 134 */ 135 TAILQ_INIT(&pcond->c_queue); 136 pcond->c_flags |= COND_FLAGS_INITED; 137 pcond->c_type = type; 138 pcond->c_mutex = NULL; 139 pcond->c_seqno = 0; 140 bzero(&pcond->c_lock, sizeof(pcond->c_lock)); 141 142 *cond = pcond; 143 144 return (0); 145 } 146 147 int 148 _pthread_cond_destroy(pthread_cond_t *cond) 149 { 150 /* 151 * Short circuit for a statically initialized condvar 152 * that is being destroyed without having been used. 153 */ 154 if (*cond == PTHREAD_COND_INITIALIZER) 155 return (0); 156 157 COND_LOCK(*cond); 158 159 /* 160 * Free the memory allocated for the condition 161 * variable structure: 162 */ 163 free(*cond); 164 165 /* 166 * NULL the caller's pointer now that the condition 167 * variable has been destroyed: 168 */ 169 *cond = NULL; 170 171 return (0); 172 } 173 174 int 175 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 176 { 177 int rval; 178 179 rval = cond_wait_common(cond, mutex, NULL); 180 181 /* This should never happen. */ 182 if (rval == ETIMEDOUT) 183 abort(); 184 185 return (rval); 186 } 187 188 int 189 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 190 const struct timespec * abstime) 191 { 192 if (abstime == NULL || abstime->tv_nsec >= 1000000000) 193 return (EINVAL); 194 195 return (cond_wait_common(cond, mutex, abstime)); 196 } 197 198 static int 199 cond_wait_common(pthread_cond_t * cond, pthread_mutex_t * mutex, 200 const struct timespec * abstime) 201 { 202 int rval = 0; 203 int mtxrval; 204 205 206 if (cond == NULL) 207 return (EINVAL); 208 /* 209 * If the condition variable is statically initialized, perform dynamic 210 * initialization. 211 */ 212 if (*cond == PTHREAD_COND_INITIALIZER && (rval = cond_init(cond)) != 0) 213 return (rval); 214 215 if ((*cond)->c_type != COND_TYPE_FAST) 216 return (EINVAL); 217 218 COND_LOCK(*cond); 219 220 /* 221 * If the condvar was statically allocated, properly 222 * initialize the tail queue. 223 */ 224 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { 225 TAILQ_INIT(&(*cond)->c_queue); 226 (*cond)->c_flags |= COND_FLAGS_INITED; 227 } 228 229 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && 230 ((*cond)->c_mutex != *mutex))) { 231 COND_UNLOCK(*cond); 232 return (EINVAL); 233 } 234 /* Remember the mutex */ 235 (*cond)->c_mutex = *mutex; 236 237 _thread_enter_cancellation_point(); 238 if ((rval = _mutex_cv_unlock(mutex)) != 0) { 239 if (rval == -1){ 240 printf("mutex unlock by condvar failed!"); 241 fflush(stdout); 242 abort(); 243 } 244 _thread_leave_cancellation_point(); 245 COND_UNLOCK(*cond); 246 return (rval); 247 } 248 249 /* 250 * We need to protect the queue operations. It also 251 * protects the pthread flag field. This is 252 * dropped before calling _thread_suspend() and reaquired 253 * when we return. 254 */ 255 PTHREAD_LOCK(curthread); 256 257 /* 258 * Queue the running thread on the condition 259 * variable and wait to be signaled. 260 */ 261 cond_queue_enq(*cond, curthread); 262 do { 263 PTHREAD_UNLOCK(curthread); 264 COND_UNLOCK(*cond); 265 if (curthread->cancellation == CS_PENDING) { 266 /* 267 * Posix says we must lock the mutex 268 * even if we're being canceled. 269 */ 270 _mutex_cv_lock(mutex); 271 _thread_leave_cancellation_point(); 272 PANIC("Shouldn't have come back."); 273 } 274 rval = _thread_suspend(curthread, (struct timespec *)abstime); 275 if (rval != 0 && rval != ETIMEDOUT && rval != EINTR) { 276 printf("thread suspend returned an invalid value"); 277 fflush(stdout); 278 abort(); 279 } 280 COND_LOCK(*cond); 281 PTHREAD_LOCK(curthread); 282 if (rval == ETIMEDOUT) { 283 /* 284 * Condition may have been signaled between the 285 * time the thread timed out and locked the condvar. 286 * If it wasn't, manually remove it from the queue. 287 */ 288 if ((curthread->flags & PTHREAD_FLAGS_IN_CONDQ) == 0) 289 rval = 0; 290 else 291 cond_queue_remove(*cond, curthread); 292 } 293 } while ((curthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0); 294 295 PTHREAD_UNLOCK(curthread); 296 COND_UNLOCK(*cond); 297 mtxrval = _mutex_cv_lock(mutex); 298 299 /* If the mutex failed return that error. */ 300 if (mtxrval == -1) { 301 printf("mutex lock from condvar failed!"); 302 fflush(stdout); 303 abort(); 304 } 305 if (mtxrval != 0) 306 rval = mtxrval; 307 308 _thread_leave_cancellation_point(); 309 return (rval); 310 } 311 312 int 313 _pthread_cond_signal(pthread_cond_t * cond) 314 { 315 return (cond_signal(cond, 0)); 316 } 317 318 int 319 _pthread_cond_broadcast(pthread_cond_t * cond) 320 { 321 return (cond_signal(cond, 1)); 322 } 323 324 static int 325 cond_signal(pthread_cond_t * cond, int broadcast) 326 { 327 int rval = 0; 328 pthread_t pthread; 329 330 if (cond == NULL) 331 return (EINVAL); 332 /* 333 * If the condition variable is statically initialized, perform dynamic 334 * initialization. 335 */ 336 if (*cond == PTHREAD_COND_INITIALIZER && (rval = cond_init(cond)) != 0) 337 return (rval); 338 339 if ((*cond)->c_type != COND_TYPE_FAST) 340 return (EINVAL); 341 COND_LOCK(*cond); 342 343 /* 344 * Enter a loop to bring all (or only one) threads off the 345 * condition queue: 346 */ 347 do { 348 /* 349 * Wake up the signaled thread. It will be returned 350 * to us locked. 351 */ 352 if ((pthread = cond_queue_deq(*cond)) != NULL) { 353 PTHREAD_WAKE(pthread); 354 PTHREAD_UNLOCK(pthread); 355 } 356 } while (broadcast && pthread != NULL); 357 358 COND_UNLOCK(*cond); 359 return (rval); 360 } 361 362 void 363 _cond_wait_backout(pthread_t pthread) 364 { 365 pthread_cond_t cond; 366 367 cond = pthread->data.cond; 368 if (cond == NULL) 369 return; 370 371 /* Process according to condition variable type: */ 372 switch (cond->c_type) { 373 /* Fast condition variable: */ 374 case COND_TYPE_FAST: 375 cond_queue_remove(cond, pthread); 376 break; 377 default: 378 break; 379 } 380 } 381 382 /* 383 * Dequeue a waiting thread from the head of a condition queue in 384 * descending priority order. 385 */ 386 static pthread_t 387 cond_queue_deq(pthread_cond_t cond) 388 { 389 pthread_t pthread; 390 391 while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) { 392 PTHREAD_LOCK(pthread); 393 cond_queue_remove(cond, pthread); 394 395 /* 396 * Only exit the loop when we find a thread 397 * that hasn't been canceled. 398 */ 399 if (pthread->cancellation == CS_NULL) 400 break; 401 else 402 PTHREAD_UNLOCK(pthread); 403 } 404 405 return(pthread); 406 } 407 408 /* 409 * Remove a waiting thread from a condition queue in descending priority 410 * order. 411 */ 412 static void 413 cond_queue_remove(pthread_cond_t cond, pthread_t pthread) 414 { 415 /* 416 * Because pthread_cond_timedwait() can timeout as well 417 * as be signaled by another thread, it is necessary to 418 * guard against removing the thread from the queue if 419 * it isn't in the queue. 420 */ 421 if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) { 422 TAILQ_REMOVE(&cond->c_queue, pthread, sqe); 423 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ; 424 } 425 /* Check for no more waiters. */ 426 if (TAILQ_FIRST(&cond->c_queue) == NULL) 427 cond->c_mutex = NULL; 428 } 429 430 /* 431 * Enqueue a waiting thread to a condition queue in descending priority 432 * order. 433 */ 434 static void 435 cond_queue_enq(pthread_cond_t cond, pthread_t pthread) 436 { 437 pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head); 438 char *name; 439 440 name = pthread->name ? pthread->name : "unknown"; 441 if ((pthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0) 442 _thread_printf(2, "Thread (%s:%ld) already on condq\n", 443 pthread->name, pthread->thr_id); 444 if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) 445 _thread_printf(2, "Thread (%s:%ld) already on mutexq\n", 446 pthread->name, pthread->thr_id); 447 PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread); 448 449 /* 450 * For the common case of all threads having equal priority, 451 * we perform a quick check against the priority of the thread 452 * at the tail of the queue. 453 */ 454 if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) 455 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe); 456 else { 457 tid = TAILQ_FIRST(&cond->c_queue); 458 while (pthread->active_priority <= tid->active_priority) 459 tid = TAILQ_NEXT(tid, sqe); 460 TAILQ_INSERT_BEFORE(tid, pthread, sqe); 461 } 462 pthread->flags |= PTHREAD_FLAGS_IN_CONDQ; 463 pthread->data.cond = cond; 464 } 465 466 static inline int 467 cond_init(pthread_cond_t *cond) 468 { 469 int error = 0; 470 _SPINLOCK(&static_cond_lock); 471 if (*cond == PTHREAD_COND_INITIALIZER) 472 error = _pthread_cond_init(cond, NULL); 473 _SPINUNLOCK(&static_cond_lock); 474 return (error); 475 } 476 477