tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */