ptsynch.c (31526B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* 7 ** File: ptsynch.c 8 ** Descritpion: Implemenation for thread synchronization using pthreads 9 ** Exports: prlock.h, prcvar.h, prmon.h, prcmon.h 10 */ 11 12 #if defined(_PR_PTHREADS) 13 14 # include "primpl.h" 15 # include "obsolete/prsem.h" 16 17 # include <string.h> 18 # include <pthread.h> 19 # include <sys/time.h> 20 21 static pthread_mutexattr_t _pt_mattr; 22 static pthread_condattr_t _pt_cvar_attr; 23 24 # if defined(DEBUG) 25 extern PTDebug pt_debug; /* this is shared between several modules */ 26 # endif /* defined(DEBUG) */ 27 28 # if defined(FREEBSD) 29 /* 30 * On older versions of FreeBSD, pthread_mutex_trylock returns EDEADLK. 31 * Newer versions return EBUSY. We still need to support both. 32 */ 33 static int pt_pthread_mutex_is_locked(pthread_mutex_t* m) { 34 int rv = pthread_mutex_trylock(m); 35 return (EBUSY == rv || EDEADLK == rv); 36 } 37 # endif 38 39 /**************************************************************/ 40 /**************************************************************/ 41 /*****************************LOCKS****************************/ 42 /**************************************************************/ 43 /**************************************************************/ 44 45 void _PR_InitLocks(void) { 46 int rv; 47 rv = _PT_PTHREAD_MUTEXATTR_INIT(&_pt_mattr); 48 PR_ASSERT(0 == rv); 49 50 # if (defined(LINUX) && \ 51 (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))) || \ 52 (defined(FREEBSD) && __FreeBSD_version > 700055) 53 rv = pthread_mutexattr_settype(&_pt_mattr, PTHREAD_MUTEX_ADAPTIVE_NP); 54 PR_ASSERT(0 == rv); 55 # endif 56 57 rv = _PT_PTHREAD_CONDATTR_INIT(&_pt_cvar_attr); 58 PR_ASSERT(0 == rv); 59 } 60 61 static void pt_PostNotifies(PRLock* lock, PRBool unlock) { 62 PRIntn index, rv; 63 _PT_Notified post; 64 _PT_Notified *notified, *prev = NULL; 65 /* 66 * Time to actually notify any conditions that were affected 67 * while the lock was held. Get a copy of the list that's in 68 * the lock structure and then zero the original. If it's 69 * linked to other such structures, we own that storage. 70 */ 71 post = lock->notified; /* a safe copy; we own the lock */ 72 73 # if defined(DEBUG) 74 memset(&lock->notified, 0, sizeof(_PT_Notified)); /* reset */ 75 # else 76 lock->notified.length = 0; /* these are really sufficient */ 77 lock->notified.link = NULL; 78 # endif 79 80 /* should (may) we release lock before notifying? */ 81 if (unlock) { 82 rv = pthread_mutex_unlock(&lock->mutex); 83 PR_ASSERT(0 == rv); 84 } 85 86 notified = &post; /* this is where we start */ 87 do { 88 for (index = 0; index < notified->length; ++index) { 89 PRCondVar* cv = notified->cv[index].cv; 90 PR_ASSERT(NULL != cv); 91 PR_ASSERT(0 != notified->cv[index].times); 92 if (-1 == notified->cv[index].times) { 93 rv = pthread_cond_broadcast(&cv->cv); 94 PR_ASSERT(0 == rv); 95 } else { 96 while (notified->cv[index].times-- > 0) { 97 rv = pthread_cond_signal(&cv->cv); 98 PR_ASSERT(0 == rv); 99 } 100 } 101 # if defined(DEBUG) 102 pt_debug.cvars_notified += 1; 103 if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) { 104 pt_debug.delayed_cv_deletes += 1; 105 PR_DestroyCondVar(cv); 106 } 107 # else /* defined(DEBUG) */ 108 if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) { 109 PR_DestroyCondVar(cv); 110 } 111 # endif /* defined(DEBUG) */ 112 } 113 prev = notified; 114 notified = notified->link; 115 if (&post != prev) { 116 PR_DELETE(prev); 117 } 118 } while (NULL != notified); 119 } /* pt_PostNotifies */ 120 121 PR_IMPLEMENT(PRLock*) PR_NewLock(void) { 122 PRIntn rv; 123 PRLock* lock; 124 125 if (!_pr_initialized) { 126 _PR_ImplicitInitialization(); 127 } 128 129 lock = PR_NEWZAP(PRLock); 130 if (lock != NULL) { 131 rv = _PT_PTHREAD_MUTEX_INIT(lock->mutex, _pt_mattr); 132 PR_ASSERT(0 == rv); 133 } 134 # if defined(DEBUG) 135 pt_debug.locks_created += 1; 136 # endif 137 return lock; 138 } /* PR_NewLock */ 139 140 PR_IMPLEMENT(void) PR_DestroyLock(PRLock* lock) { 141 PRIntn rv; 142 PR_ASSERT(NULL != lock); 143 PR_ASSERT(PR_FALSE == lock->locked); 144 PR_ASSERT(0 == lock->notified.length); 145 PR_ASSERT(NULL == lock->notified.link); 146 rv = pthread_mutex_destroy(&lock->mutex); 147 PR_ASSERT(0 == rv); 148 # if defined(DEBUG) 149 memset(lock, 0xaf, sizeof(PRLock)); 150 pt_debug.locks_destroyed += 1; 151 # endif 152 PR_Free(lock); 153 } /* PR_DestroyLock */ 154 155 PR_IMPLEMENT(void) PR_Lock(PRLock* lock) { 156 /* Nb: PR_Lock must not call PR_GetCurrentThread to access the |id| or 157 * |tid| field of the current thread's PRThread structure because 158 * _pt_root calls PR_Lock before setting thred->id and thred->tid. */ 159 PRIntn rv; 160 PR_ASSERT(lock != NULL); 161 rv = pthread_mutex_lock(&lock->mutex); 162 PR_ASSERT(0 == rv); 163 PR_ASSERT(0 == lock->notified.length); 164 PR_ASSERT(NULL == lock->notified.link); 165 PR_ASSERT(PR_FALSE == lock->locked); 166 /* Nb: the order of the next two statements is not critical to 167 * the correctness of PR_AssertCurrentThreadOwnsLock(), but 168 * this particular order makes the assertion more likely to 169 * catch errors. */ 170 lock->owner = pthread_self(); 171 lock->locked = PR_TRUE; 172 # if defined(DEBUG) 173 pt_debug.locks_acquired += 1; 174 # endif 175 } /* PR_Lock */ 176 177 PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock* lock) { 178 pthread_t self = pthread_self(); 179 PRIntn rv; 180 181 PR_ASSERT(lock != NULL); 182 PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(lock->mutex)); 183 PR_ASSERT(PR_TRUE == lock->locked); 184 PR_ASSERT(pthread_equal(lock->owner, self)); 185 186 if (!lock->locked || !pthread_equal(lock->owner, self)) { 187 return PR_FAILURE; 188 } 189 190 lock->locked = PR_FALSE; 191 if (0 == lock->notified.length) /* shortcut */ 192 { 193 rv = pthread_mutex_unlock(&lock->mutex); 194 PR_ASSERT(0 == rv); 195 } else { 196 pt_PostNotifies(lock, PR_TRUE); 197 } 198 199 # if defined(DEBUG) 200 pt_debug.locks_released += 1; 201 # endif 202 return PR_SUCCESS; 203 } /* PR_Unlock */ 204 205 PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock* lock) { 206 /* Nb: the order of the |locked| and |owner==me| checks is not critical 207 * to the correctness of PR_AssertCurrentThreadOwnsLock(), but 208 * this particular order makes the assertion more likely to 209 * catch errors. */ 210 PR_ASSERT(lock->locked && pthread_equal(lock->owner, pthread_self())); 211 } 212 213 /**************************************************************/ 214 /**************************************************************/ 215 /***************************CONDITIONS*************************/ 216 /**************************************************************/ 217 /**************************************************************/ 218 219 /* 220 * This code is used to compute the absolute time for the wakeup. 221 * It's moderately ugly, so it's defined here and called in a 222 * couple of places. 223 */ 224 # define PT_NANOPERMICRO 1000UL 225 # define PT_BILLION 1000000000UL 226 227 static PRIntn pt_TimedWait(pthread_cond_t* cv, pthread_mutex_t* ml, 228 PRIntervalTime timeout) { 229 int rv; 230 struct timeval now; 231 struct timespec tmo; 232 PRUint32 ticks = PR_TicksPerSecond(); 233 234 tmo.tv_sec = (PRInt32)(timeout / ticks); 235 tmo.tv_nsec = (PRInt32)(timeout - (tmo.tv_sec * ticks)); 236 tmo.tv_nsec = 237 (PRInt32)PR_IntervalToMicroseconds(PT_NANOPERMICRO * tmo.tv_nsec); 238 239 /* pthreads wants this in absolute time, off we go ... */ 240 (void)GETTIMEOFDAY(&now); 241 /* that one's usecs, this one's nsecs - grrrr! */ 242 tmo.tv_sec += now.tv_sec; 243 tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec); 244 tmo.tv_sec += tmo.tv_nsec / PT_BILLION; 245 tmo.tv_nsec %= PT_BILLION; 246 247 rv = pthread_cond_timedwait(cv, ml, &tmo); 248 249 /* NSPR doesn't report timeouts */ 250 return (rv == ETIMEDOUT) ? 0 : rv; 251 } /* pt_TimedWait */ 252 253 /* 254 * Notifies just get posted to the protecting mutex. The 255 * actual notification is done when the lock is released so that 256 * MP systems don't contend for a lock that they can't have. 257 */ 258 static void pt_PostNotifyToCvar(PRCondVar* cvar, PRBool broadcast) { 259 PRIntn index = 0; 260 _PT_Notified* notified = &cvar->lock->notified; 261 262 PR_ASSERT(PR_TRUE == cvar->lock->locked); 263 PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self())); 264 PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex)); 265 266 while (1) { 267 for (index = 0; index < notified->length; ++index) { 268 if (notified->cv[index].cv == cvar) { 269 if (broadcast) { 270 notified->cv[index].times = -1; 271 } else if (-1 != notified->cv[index].times) { 272 notified->cv[index].times += 1; 273 } 274 return; /* we're finished */ 275 } 276 } 277 /* if not full, enter new CV in this array */ 278 if (notified->length < PT_CV_NOTIFIED_LENGTH) { 279 break; 280 } 281 282 /* if there's no link, create an empty array and link it */ 283 if (NULL == notified->link) { 284 notified->link = PR_NEWZAP(_PT_Notified); 285 } 286 notified = notified->link; 287 } 288 289 /* A brand new entry in the array */ 290 (void)PR_ATOMIC_INCREMENT(&cvar->notify_pending); 291 notified->cv[index].times = (broadcast) ? -1 : 1; 292 notified->cv[index].cv = cvar; 293 notified->length += 1; 294 } /* pt_PostNotifyToCvar */ 295 296 PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock* lock) { 297 PRCondVar* cv = PR_NEW(PRCondVar); 298 PR_ASSERT(lock != NULL); 299 if (cv != NULL) { 300 int rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr); 301 PR_ASSERT(0 == rv); 302 if (0 == rv) { 303 cv->lock = lock; 304 cv->notify_pending = 0; 305 # if defined(DEBUG) 306 pt_debug.cvars_created += 1; 307 # endif 308 } else { 309 PR_DELETE(cv); 310 cv = NULL; 311 } 312 } 313 return cv; 314 } /* PR_NewCondVar */ 315 316 PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar* cvar) { 317 if (0 > PR_ATOMIC_DECREMENT(&cvar->notify_pending)) { 318 PRIntn rv = pthread_cond_destroy(&cvar->cv); 319 # if defined(DEBUG) 320 PR_ASSERT(0 == rv); 321 memset(cvar, 0xaf, sizeof(PRCondVar)); 322 pt_debug.cvars_destroyed += 1; 323 # else 324 (void)rv; 325 # endif 326 PR_Free(cvar); 327 } 328 } /* PR_DestroyCondVar */ 329 330 PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar* cvar, PRIntervalTime timeout) { 331 PRIntn rv; 332 PRThread* thred = PR_GetCurrentThread(); 333 334 PR_ASSERT(cvar != NULL); 335 /* We'd better be locked */ 336 PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex)); 337 PR_ASSERT(PR_TRUE == cvar->lock->locked); 338 /* and it better be by us */ 339 PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self())); 340 341 if (_PT_THREAD_INTERRUPTED(thred)) { 342 goto aborted; 343 } 344 345 /* 346 * The thread waiting is used for PR_Interrupt 347 */ 348 thred->waiting = cvar; /* this is where we're waiting */ 349 350 /* 351 * If we have pending notifies, post them now. 352 * 353 * This is not optimal. We're going to post these notifies 354 * while we're holding the lock. That means on MP systems 355 * that they are going to collide for the lock that we will 356 * hold until we actually wait. 357 */ 358 if (0 != cvar->lock->notified.length) { 359 pt_PostNotifies(cvar->lock, PR_FALSE); 360 } 361 362 /* 363 * We're surrendering the lock, so clear out the locked field. 364 */ 365 cvar->lock->locked = PR_FALSE; 366 367 if (timeout == PR_INTERVAL_NO_TIMEOUT) { 368 rv = pthread_cond_wait(&cvar->cv, &cvar->lock->mutex); 369 } else { 370 rv = pt_TimedWait(&cvar->cv, &cvar->lock->mutex, timeout); 371 } 372 373 /* We just got the lock back - this better be empty */ 374 PR_ASSERT(PR_FALSE == cvar->lock->locked); 375 cvar->lock->locked = PR_TRUE; 376 cvar->lock->owner = pthread_self(); 377 378 PR_ASSERT(0 == cvar->lock->notified.length); 379 thred->waiting = NULL; /* and now we're not */ 380 if (_PT_THREAD_INTERRUPTED(thred)) { 381 goto aborted; 382 } 383 if (rv != 0) { 384 _PR_MD_MAP_DEFAULT_ERROR(rv); 385 return PR_FAILURE; 386 } 387 return PR_SUCCESS; 388 389 aborted: 390 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); 391 thred->state &= ~PT_THREAD_ABORTED; 392 return PR_FAILURE; 393 } /* PR_WaitCondVar */ 394 395 PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar* cvar) { 396 PR_ASSERT(cvar != NULL); 397 pt_PostNotifyToCvar(cvar, PR_FALSE); 398 return PR_SUCCESS; 399 } /* PR_NotifyCondVar */ 400 401 PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar* cvar) { 402 PR_ASSERT(cvar != NULL); 403 pt_PostNotifyToCvar(cvar, PR_TRUE); 404 return PR_SUCCESS; 405 } /* PR_NotifyAllCondVar */ 406 407 /**************************************************************/ 408 /**************************************************************/ 409 /***************************MONITORS***************************/ 410 /**************************************************************/ 411 /**************************************************************/ 412 413 /* 414 * Notifies just get posted to the monitor. The actual notification is done 415 * when the monitor is fully exited so that MP systems don't contend for a 416 * monitor that they can't enter. 417 */ 418 static void pt_PostNotifyToMonitor(PRMonitor* mon, PRBool broadcast) { 419 PR_ASSERT(NULL != mon); 420 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mon); 421 422 /* mon->notifyTimes is protected by the monitor, so we don't need to 423 * acquire mon->lock. 424 */ 425 if (broadcast) { 426 mon->notifyTimes = -1; 427 } else if (-1 != mon->notifyTimes) { 428 mon->notifyTimes += 1; 429 } 430 } /* pt_PostNotifyToMonitor */ 431 432 static void pt_PostNotifiesFromMonitor(pthread_cond_t* cv, PRIntn times) { 433 PRIntn rv; 434 435 /* 436 * Time to actually notify any waits that were affected while the monitor 437 * was entered. 438 */ 439 PR_ASSERT(NULL != cv); 440 PR_ASSERT(0 != times); 441 if (-1 == times) { 442 rv = pthread_cond_broadcast(cv); 443 PR_ASSERT(0 == rv); 444 } else { 445 while (times-- > 0) { 446 rv = pthread_cond_signal(cv); 447 PR_ASSERT(0 == rv); 448 } 449 } 450 } /* pt_PostNotifiesFromMonitor */ 451 452 PR_IMPLEMENT(PRMonitor*) PR_NewMonitor(void) { 453 PRMonitor* mon; 454 int rv; 455 456 if (!_pr_initialized) { 457 _PR_ImplicitInitialization(); 458 } 459 460 mon = PR_NEWZAP(PRMonitor); 461 if (mon == NULL) { 462 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 463 return NULL; 464 } 465 466 rv = _PT_PTHREAD_MUTEX_INIT(mon->lock, _pt_mattr); 467 PR_ASSERT(0 == rv); 468 if (0 != rv) { 469 goto error1; 470 } 471 472 _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); 473 474 rv = _PT_PTHREAD_COND_INIT(mon->entryCV, _pt_cvar_attr); 475 PR_ASSERT(0 == rv); 476 if (0 != rv) { 477 goto error2; 478 } 479 480 rv = _PT_PTHREAD_COND_INIT(mon->waitCV, _pt_cvar_attr); 481 PR_ASSERT(0 == rv); 482 if (0 != rv) { 483 goto error3; 484 } 485 486 mon->notifyTimes = 0; 487 mon->entryCount = 0; 488 mon->refCount = 1; 489 mon->name = NULL; 490 return mon; 491 492 error3: 493 pthread_cond_destroy(&mon->entryCV); 494 error2: 495 pthread_mutex_destroy(&mon->lock); 496 error1: 497 PR_Free(mon); 498 _PR_MD_MAP_DEFAULT_ERROR(rv); 499 return NULL; 500 } /* PR_NewMonitor */ 501 502 PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name) { 503 PRMonitor* mon = PR_NewMonitor(); 504 if (mon) { 505 mon->name = name; 506 } 507 return mon; 508 } 509 510 PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor* mon) { 511 int rv; 512 513 PR_ASSERT(mon != NULL); 514 if (PR_ATOMIC_DECREMENT(&mon->refCount) == 0) { 515 rv = pthread_cond_destroy(&mon->waitCV); 516 PR_ASSERT(0 == rv); 517 rv = pthread_cond_destroy(&mon->entryCV); 518 PR_ASSERT(0 == rv); 519 rv = pthread_mutex_destroy(&mon->lock); 520 PR_ASSERT(0 == rv); 521 # if defined(DEBUG) 522 memset(mon, 0xaf, sizeof(PRMonitor)); 523 # endif 524 PR_Free(mon); 525 } 526 } /* PR_DestroyMonitor */ 527 528 /* The GC uses this; it is quite arguably a bad interface. I'm just 529 * duplicating it for now - XXXMB 530 */ 531 PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor* mon) { 532 pthread_t self = pthread_self(); 533 PRIntn rv; 534 PRIntn count = 0; 535 536 rv = pthread_mutex_lock(&mon->lock); 537 PR_ASSERT(0 == rv); 538 if (pthread_equal(mon->owner, self)) { 539 count = mon->entryCount; 540 } 541 rv = pthread_mutex_unlock(&mon->lock); 542 PR_ASSERT(0 == rv); 543 return count; 544 } 545 546 PR_IMPLEMENT(void) PR_AssertCurrentThreadInMonitor(PRMonitor* mon) { 547 # if defined(DEBUG) || defined(FORCE_PR_ASSERT) 548 PRIntn rv; 549 550 rv = pthread_mutex_lock(&mon->lock); 551 PR_ASSERT(0 == rv); 552 PR_ASSERT(mon->entryCount != 0 && pthread_equal(mon->owner, pthread_self())); 553 rv = pthread_mutex_unlock(&mon->lock); 554 PR_ASSERT(0 == rv); 555 # endif 556 } 557 558 PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor* mon) { 559 pthread_t self = pthread_self(); 560 PRIntn rv; 561 562 PR_ASSERT(mon != NULL); 563 rv = pthread_mutex_lock(&mon->lock); 564 PR_ASSERT(0 == rv); 565 if (mon->entryCount != 0) { 566 if (pthread_equal(mon->owner, self)) { 567 goto done; 568 } 569 while (mon->entryCount != 0) { 570 rv = pthread_cond_wait(&mon->entryCV, &mon->lock); 571 PR_ASSERT(0 == rv); 572 } 573 } 574 /* and now I have the monitor */ 575 PR_ASSERT(0 == mon->notifyTimes); 576 PR_ASSERT(_PT_PTHREAD_THR_HANDLE_IS_INVALID(mon->owner)); 577 _PT_PTHREAD_COPY_THR_HANDLE(self, mon->owner); 578 579 done: 580 mon->entryCount += 1; 581 rv = pthread_mutex_unlock(&mon->lock); 582 PR_ASSERT(0 == rv); 583 } /* PR_EnterMonitor */ 584 585 PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor* mon) { 586 pthread_t self = pthread_self(); 587 PRIntn rv; 588 PRBool notifyEntryWaiter = PR_FALSE; 589 PRIntn notifyTimes = 0; 590 591 PR_ASSERT(mon != NULL); 592 rv = pthread_mutex_lock(&mon->lock); 593 PR_ASSERT(0 == rv); 594 /* the entries should be > 0 and we'd better be the owner */ 595 PR_ASSERT(mon->entryCount > 0); 596 PR_ASSERT(pthread_equal(mon->owner, self)); 597 if (mon->entryCount == 0 || !pthread_equal(mon->owner, self)) { 598 rv = pthread_mutex_unlock(&mon->lock); 599 PR_ASSERT(0 == rv); 600 return PR_FAILURE; 601 } 602 603 mon->entryCount -= 1; /* reduce by one */ 604 if (mon->entryCount == 0) { 605 /* and if it transitioned to zero - notify an entry waiter */ 606 /* make the owner unknown */ 607 _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); 608 notifyEntryWaiter = PR_TRUE; 609 notifyTimes = mon->notifyTimes; 610 mon->notifyTimes = 0; 611 /* We will access the members of 'mon' after unlocking mon->lock. 612 * Add a reference. */ 613 PR_ATOMIC_INCREMENT(&mon->refCount); 614 } 615 rv = pthread_mutex_unlock(&mon->lock); 616 PR_ASSERT(0 == rv); 617 if (notifyEntryWaiter) { 618 if (notifyTimes) { 619 pt_PostNotifiesFromMonitor(&mon->waitCV, notifyTimes); 620 } 621 rv = pthread_cond_signal(&mon->entryCV); 622 PR_ASSERT(0 == rv); 623 /* We are done accessing the members of 'mon'. Release the 624 * reference. */ 625 PR_DestroyMonitor(mon); 626 } 627 return PR_SUCCESS; 628 } /* PR_ExitMonitor */ 629 630 PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor* mon, PRIntervalTime timeout) { 631 PRStatus rv; 632 PRUint32 saved_entries; 633 pthread_t saved_owner; 634 635 PR_ASSERT(mon != NULL); 636 rv = pthread_mutex_lock(&mon->lock); 637 PR_ASSERT(0 == rv); 638 /* the entries better be positive */ 639 PR_ASSERT(mon->entryCount > 0); 640 /* and it better be owned by us */ 641 PR_ASSERT(pthread_equal(mon->owner, pthread_self())); 642 643 /* tuck these away 'till later */ 644 saved_entries = mon->entryCount; 645 mon->entryCount = 0; 646 _PT_PTHREAD_COPY_THR_HANDLE(mon->owner, saved_owner); 647 _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); 648 /* 649 * If we have pending notifies, post them now. 650 * 651 * This is not optimal. We're going to post these notifies 652 * while we're holding the lock. That means on MP systems 653 * that they are going to collide for the lock that we will 654 * hold until we actually wait. 655 */ 656 if (0 != mon->notifyTimes) { 657 pt_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes); 658 mon->notifyTimes = 0; 659 } 660 rv = pthread_cond_signal(&mon->entryCV); 661 PR_ASSERT(0 == rv); 662 663 if (timeout == PR_INTERVAL_NO_TIMEOUT) { 664 rv = pthread_cond_wait(&mon->waitCV, &mon->lock); 665 } else { 666 rv = pt_TimedWait(&mon->waitCV, &mon->lock, timeout); 667 } 668 PR_ASSERT(0 == rv); 669 670 while (mon->entryCount != 0) { 671 rv = pthread_cond_wait(&mon->entryCV, &mon->lock); 672 PR_ASSERT(0 == rv); 673 } 674 PR_ASSERT(0 == mon->notifyTimes); 675 /* reinstate the interesting information */ 676 mon->entryCount = saved_entries; 677 _PT_PTHREAD_COPY_THR_HANDLE(saved_owner, mon->owner); 678 679 rv = pthread_mutex_unlock(&mon->lock); 680 PR_ASSERT(0 == rv); 681 return rv; 682 } /* PR_Wait */ 683 684 PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor* mon) { 685 pt_PostNotifyToMonitor(mon, PR_FALSE); 686 return PR_SUCCESS; 687 } /* PR_Notify */ 688 689 PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor* mon) { 690 pt_PostNotifyToMonitor(mon, PR_TRUE); 691 return PR_SUCCESS; 692 } /* PR_NotifyAll */ 693 694 /**************************************************************/ 695 /**************************************************************/ 696 /**************************SEMAPHORES**************************/ 697 /**************************************************************/ 698 /**************************************************************/ 699 PR_IMPLEMENT(void) PR_PostSem(PRSemaphore* semaphore) { 700 static PRBool unwarned = PR_TRUE; 701 if (unwarned) 702 unwarned = _PR_Obsolete("PR_PostSem", "locks & condition variables"); 703 PR_Lock(semaphore->cvar->lock); 704 PR_NotifyCondVar(semaphore->cvar); 705 semaphore->count += 1; 706 PR_Unlock(semaphore->cvar->lock); 707 } /* PR_PostSem */ 708 709 PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore* semaphore) { 710 PRStatus status = PR_SUCCESS; 711 static PRBool unwarned = PR_TRUE; 712 if (unwarned) 713 unwarned = _PR_Obsolete("PR_WaitSem", "locks & condition variables"); 714 PR_Lock(semaphore->cvar->lock); 715 while ((semaphore->count == 0) && (PR_SUCCESS == status)) { 716 status = PR_WaitCondVar(semaphore->cvar, PR_INTERVAL_NO_TIMEOUT); 717 } 718 if (PR_SUCCESS == status) { 719 semaphore->count -= 1; 720 } 721 PR_Unlock(semaphore->cvar->lock); 722 return status; 723 } /* PR_WaitSem */ 724 725 PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore* semaphore) { 726 static PRBool unwarned = PR_TRUE; 727 if (unwarned) 728 unwarned = _PR_Obsolete("PR_DestroySem", "locks & condition variables"); 729 PR_DestroyLock(semaphore->cvar->lock); 730 PR_DestroyCondVar(semaphore->cvar); 731 PR_Free(semaphore); 732 } /* PR_DestroySem */ 733 734 PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value) { 735 PRSemaphore* semaphore; 736 static PRBool unwarned = PR_TRUE; 737 if (!_pr_initialized) { 738 _PR_ImplicitInitialization(); 739 } 740 741 if (unwarned) 742 unwarned = _PR_Obsolete("PR_NewSem", "locks & condition variables"); 743 744 semaphore = PR_NEWZAP(PRSemaphore); 745 if (NULL != semaphore) { 746 PRLock* lock = PR_NewLock(); 747 if (NULL != lock) { 748 semaphore->cvar = PR_NewCondVar(lock); 749 if (NULL != semaphore->cvar) { 750 semaphore->count = value; 751 return semaphore; 752 } 753 PR_DestroyLock(lock); 754 } 755 PR_Free(semaphore); 756 } 757 return NULL; 758 } 759 760 /* 761 * Define the interprocess named semaphore functions. 762 * There are three implementations: 763 * 1. POSIX semaphore based; 764 * 2. System V semaphore based; 765 * 3. unsupported (fails with PR_NOT_IMPLEMENTED_ERROR). 766 */ 767 768 # ifdef _PR_HAVE_POSIX_SEMAPHORES 769 # include <fcntl.h> 770 771 PR_IMPLEMENT(PRSem*) 772 PR_OpenSemaphore(const char* name, PRIntn flags, PRIntn mode, PRUintn value) { 773 PRSem* sem; 774 char osname[PR_IPC_NAME_SIZE]; 775 776 if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) == 777 PR_FAILURE) { 778 return NULL; 779 } 780 781 sem = PR_NEW(PRSem); 782 if (NULL == sem) { 783 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 784 return NULL; 785 } 786 787 if (flags & PR_SEM_CREATE) { 788 int oflag = O_CREAT; 789 790 if (flags & PR_SEM_EXCL) { 791 oflag |= O_EXCL; 792 } 793 sem->sem = sem_open(osname, oflag, mode, value); 794 } else { 795 sem->sem = sem_open(osname, 0); 796 } 797 if ((sem_t*)-1 == sem->sem) { 798 _PR_MD_MAP_DEFAULT_ERROR(errno); 799 PR_Free(sem); 800 return NULL; 801 } 802 return sem; 803 } 804 805 PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem* sem) { 806 int rv; 807 rv = sem_wait(sem->sem); 808 if (0 != rv) { 809 _PR_MD_MAP_DEFAULT_ERROR(errno); 810 return PR_FAILURE; 811 } 812 return PR_SUCCESS; 813 } 814 815 PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem* sem) { 816 int rv; 817 rv = sem_post(sem->sem); 818 if (0 != rv) { 819 _PR_MD_MAP_DEFAULT_ERROR(errno); 820 return PR_FAILURE; 821 } 822 return PR_SUCCESS; 823 } 824 825 PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem* sem) { 826 int rv; 827 rv = sem_close(sem->sem); 828 if (0 != rv) { 829 _PR_MD_MAP_DEFAULT_ERROR(errno); 830 return PR_FAILURE; 831 } 832 PR_Free(sem); 833 return PR_SUCCESS; 834 } 835 836 PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char* name) { 837 int rv; 838 char osname[PR_IPC_NAME_SIZE]; 839 840 if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) == 841 PR_FAILURE) { 842 return PR_FAILURE; 843 } 844 rv = sem_unlink(osname); 845 if (0 != rv) { 846 _PR_MD_MAP_DEFAULT_ERROR(errno); 847 return PR_FAILURE; 848 } 849 return PR_SUCCESS; 850 } 851 852 # elif defined(_PR_HAVE_SYSV_SEMAPHORES) 853 854 # include <fcntl.h> 855 # include <sys/sem.h> 856 857 /* 858 * From the semctl(2) man page in glibc 2.0 859 */ 860 # if (defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)) || \ 861 (defined(FREEBSD) && __FreeBSD_version < 1200059) || \ 862 defined(OPENBSD) || defined(DARWIN) 863 /* union semun is defined by including <sys/sem.h> */ 864 # else 865 /* according to X/OPEN we have to define it ourselves */ 866 union semun { 867 int val; 868 struct semid_ds* buf; 869 unsigned short* array; 870 }; 871 # endif 872 873 /* 874 * 'a' (97) is the final closing price of NSCP stock. 875 */ 876 # define NSPR_IPC_KEY_ID 'a' /* the id argument for ftok() */ 877 878 # define NSPR_SEM_MODE 0666 879 880 PR_IMPLEMENT(PRSem*) 881 PR_OpenSemaphore(const char* name, PRIntn flags, PRIntn mode, PRUintn value) { 882 PRSem* sem; 883 key_t key; 884 union semun arg; 885 struct sembuf sop; 886 struct semid_ds seminfo; 887 # define MAX_TRIES 60 888 PRIntn i; 889 char osname[PR_IPC_NAME_SIZE]; 890 891 if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) == 892 PR_FAILURE) { 893 return NULL; 894 } 895 896 /* Make sure the file exists before calling ftok. */ 897 if (flags & PR_SEM_CREATE) { 898 int osfd = open(osname, O_RDWR | O_CREAT, mode); 899 if (-1 == osfd) { 900 _PR_MD_MAP_OPEN_ERROR(errno); 901 return NULL; 902 } 903 if (close(osfd) == -1) { 904 _PR_MD_MAP_CLOSE_ERROR(errno); 905 return NULL; 906 } 907 } 908 key = ftok(osname, NSPR_IPC_KEY_ID); 909 if ((key_t)-1 == key) { 910 _PR_MD_MAP_DEFAULT_ERROR(errno); 911 return NULL; 912 } 913 914 sem = PR_NEW(PRSem); 915 if (NULL == sem) { 916 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 917 return NULL; 918 } 919 920 if (flags & PR_SEM_CREATE) { 921 sem->semid = semget(key, 1, mode | IPC_CREAT | IPC_EXCL); 922 if (sem->semid >= 0) { 923 /* creator of a semaphore is responsible for initializing it */ 924 arg.val = 0; 925 if (semctl(sem->semid, 0, SETVAL, arg) == -1) { 926 _PR_MD_MAP_DEFAULT_ERROR(errno); 927 PR_Free(sem); 928 return NULL; 929 } 930 /* call semop to set sem_otime to nonzero */ 931 sop.sem_num = 0; 932 sop.sem_op = value; 933 sop.sem_flg = 0; 934 if (semop(sem->semid, &sop, 1) == -1) { 935 _PR_MD_MAP_DEFAULT_ERROR(errno); 936 PR_Free(sem); 937 return NULL; 938 } 939 return sem; 940 } 941 942 if (errno != EEXIST || flags & PR_SEM_EXCL) { 943 _PR_MD_MAP_DEFAULT_ERROR(errno); 944 PR_Free(sem); 945 return NULL; 946 } 947 } 948 949 sem->semid = semget(key, 1, NSPR_SEM_MODE); 950 if (sem->semid == -1) { 951 _PR_MD_MAP_DEFAULT_ERROR(errno); 952 PR_Free(sem); 953 return NULL; 954 } 955 for (i = 0; i < MAX_TRIES; i++) { 956 arg.buf = &seminfo; 957 semctl(sem->semid, 0, IPC_STAT, arg); 958 if (seminfo.sem_otime != 0) { 959 break; 960 } 961 sleep(1); 962 } 963 if (i == MAX_TRIES) { 964 PR_SetError(PR_IO_TIMEOUT_ERROR, 0); 965 PR_Free(sem); 966 return NULL; 967 } 968 return sem; 969 } 970 971 PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem* sem) { 972 struct sembuf sop; 973 974 sop.sem_num = 0; 975 sop.sem_op = -1; 976 sop.sem_flg = 0; 977 if (semop(sem->semid, &sop, 1) == -1) { 978 _PR_MD_MAP_DEFAULT_ERROR(errno); 979 return PR_FAILURE; 980 } 981 return PR_SUCCESS; 982 } 983 984 PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem* sem) { 985 struct sembuf sop; 986 987 sop.sem_num = 0; 988 sop.sem_op = 1; 989 sop.sem_flg = 0; 990 if (semop(sem->semid, &sop, 1) == -1) { 991 _PR_MD_MAP_DEFAULT_ERROR(errno); 992 return PR_FAILURE; 993 } 994 return PR_SUCCESS; 995 } 996 997 PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem* sem) { 998 PR_Free(sem); 999 return PR_SUCCESS; 1000 } 1001 1002 PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char* name) { 1003 key_t key; 1004 int semid; 1005 /* On some systems (e.g., glibc 2.0) semctl requires a fourth argument */ 1006 union semun unused; 1007 char osname[PR_IPC_NAME_SIZE]; 1008 1009 if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) == 1010 PR_FAILURE) { 1011 return PR_FAILURE; 1012 } 1013 key = ftok(osname, NSPR_IPC_KEY_ID); 1014 if ((key_t)-1 == key) { 1015 _PR_MD_MAP_DEFAULT_ERROR(errno); 1016 return PR_FAILURE; 1017 } 1018 if (unlink(osname) == -1) { 1019 _PR_MD_MAP_UNLINK_ERROR(errno); 1020 return PR_FAILURE; 1021 } 1022 semid = semget(key, 1, NSPR_SEM_MODE); 1023 if (-1 == semid) { 1024 _PR_MD_MAP_DEFAULT_ERROR(errno); 1025 return PR_FAILURE; 1026 } 1027 unused.val = 0; 1028 if (semctl(semid, 0, IPC_RMID, unused) == -1) { 1029 _PR_MD_MAP_DEFAULT_ERROR(errno); 1030 return PR_FAILURE; 1031 } 1032 return PR_SUCCESS; 1033 } 1034 1035 # else /* neither POSIX nor System V semaphores are available */ 1036 1037 PR_IMPLEMENT(PRSem*) 1038 PR_OpenSemaphore(const char* name, PRIntn flags, PRIntn mode, PRUintn value) { 1039 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 1040 return NULL; 1041 } 1042 1043 PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem* sem) { 1044 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 1045 return PR_FAILURE; 1046 } 1047 1048 PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem* sem) { 1049 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 1050 return PR_FAILURE; 1051 } 1052 1053 PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem* sem) { 1054 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 1055 return PR_FAILURE; 1056 } 1057 1058 PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char* name) { 1059 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 1060 return PR_FAILURE; 1061 } 1062 1063 # endif /* end of interprocess named semaphore functions */ 1064 1065 /**************************************************************/ 1066 /**************************************************************/ 1067 /******************ROUTINES FOR DCE EMULATION******************/ 1068 /**************************************************************/ 1069 /**************************************************************/ 1070 1071 # include "prpdce.h" 1072 1073 PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock* lock) { 1074 PRIntn rv = pthread_mutex_trylock(&lock->mutex); 1075 if (rv == 0) { 1076 PR_ASSERT(PR_FALSE == lock->locked); 1077 lock->locked = PR_TRUE; 1078 lock->owner = pthread_self(); 1079 } 1080 /* XXX set error code? */ 1081 return (0 == rv) ? PR_SUCCESS : PR_FAILURE; 1082 } /* PRP_TryLock */ 1083 1084 PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void) { 1085 PRCondVar* cv; 1086 1087 if (!_pr_initialized) { 1088 _PR_ImplicitInitialization(); 1089 } 1090 1091 cv = PR_NEW(PRCondVar); 1092 if (cv != NULL) { 1093 int rv; 1094 rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr); 1095 PR_ASSERT(0 == rv); 1096 if (0 == rv) { 1097 cv->lock = _PR_NAKED_CV_LOCK; 1098 } else { 1099 PR_DELETE(cv); 1100 cv = NULL; 1101 } 1102 } 1103 return cv; 1104 } /* PRP_NewNakedCondVar */ 1105 1106 PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar* cvar) { 1107 int rv; 1108 rv = pthread_cond_destroy(&cvar->cv); 1109 PR_ASSERT(0 == rv); 1110 # if defined(DEBUG) 1111 memset(cvar, 0xaf, sizeof(PRCondVar)); 1112 # endif 1113 PR_Free(cvar); 1114 } /* PRP_DestroyNakedCondVar */ 1115 1116 PR_IMPLEMENT(PRStatus) 1117 PRP_NakedWait(PRCondVar* cvar, PRLock* ml, PRIntervalTime timeout) { 1118 PRIntn rv; 1119 PR_ASSERT(cvar != NULL); 1120 /* XXX do we really want to assert this in a naked wait? */ 1121 PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(ml->mutex)); 1122 if (timeout == PR_INTERVAL_NO_TIMEOUT) { 1123 rv = pthread_cond_wait(&cvar->cv, &ml->mutex); 1124 } else { 1125 rv = pt_TimedWait(&cvar->cv, &ml->mutex, timeout); 1126 } 1127 if (rv != 0) { 1128 _PR_MD_MAP_DEFAULT_ERROR(rv); 1129 return PR_FAILURE; 1130 } 1131 return PR_SUCCESS; 1132 } /* PRP_NakedWait */ 1133 1134 PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar* cvar) { 1135 int rv; 1136 PR_ASSERT(cvar != NULL); 1137 rv = pthread_cond_signal(&cvar->cv); 1138 PR_ASSERT(0 == rv); 1139 return PR_SUCCESS; 1140 } /* PRP_NakedNotify */ 1141 1142 PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar* cvar) { 1143 int rv; 1144 PR_ASSERT(cvar != NULL); 1145 rv = pthread_cond_broadcast(&cvar->cv); 1146 PR_ASSERT(0 == rv); 1147 return PR_SUCCESS; 1148 } /* PRP_NakedBroadcast */ 1149 1150 #endif /* defined(_PR_PTHREADS) */ 1151 1152 /* ptsynch.c */