nsprpub/pr/src/pthreads/ptsynch.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial