nsprpub/pr/src/threads/prmon.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 #include "primpl.h"
     8 /************************************************************************/
    10 /*
    11  * Notifies just get posted to the monitor. The actual notification is done
    12  * when the monitor is fully exited so that MP systems don't contend for a
    13  * monitor that they can't enter.
    14  */
    15 static void _PR_PostNotifyToMonitor(PRMonitor *mon, PRBool broadcast)
    16 {
    17     PR_ASSERT(mon != NULL);
    18     PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mon);
    20     /* mon->notifyTimes is protected by the monitor, so we don't need to
    21      * acquire mon->lock.
    22      */
    23     if (broadcast)
    24         mon->notifyTimes = -1;
    25     else if (mon->notifyTimes != -1)
    26         mon->notifyTimes += 1;
    27 }
    29 static void _PR_PostNotifiesFromMonitor(PRCondVar *cv, PRIntn times)
    30 {
    31     PRStatus rv;
    33     /*
    34      * Time to actually notify any waits that were affected while the monitor
    35      * was entered.
    36      */
    37     PR_ASSERT(cv != NULL);
    38     PR_ASSERT(times != 0);
    39     if (times == -1) {
    40         rv = PR_NotifyAllCondVar(cv);
    41         PR_ASSERT(rv == PR_SUCCESS);
    42     } else {
    43         while (times-- > 0) {
    44             rv = PR_NotifyCondVar(cv);
    45             PR_ASSERT(rv == PR_SUCCESS);
    46         }
    47     }
    48 }
    50 /*
    51 ** Create a new monitor.
    52 */
    53 PR_IMPLEMENT(PRMonitor*) PR_NewMonitor()
    54 {
    55     PRMonitor *mon;
    56     PRStatus rv;
    58     if (!_pr_initialized) _PR_ImplicitInitialization();
    60     mon = PR_NEWZAP(PRMonitor);
    61     if (mon == NULL) {
    62         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    63         return NULL;
    64     }
    66     rv = _PR_InitLock(&mon->lock);
    67     PR_ASSERT(rv == PR_SUCCESS);
    68     if (rv != PR_SUCCESS)
    69         goto error1;
    71     mon->owner = NULL;
    73     rv = _PR_InitCondVar(&mon->entryCV, &mon->lock);
    74     PR_ASSERT(rv == PR_SUCCESS);
    75     if (rv != PR_SUCCESS)
    76         goto error2;
    78     rv = _PR_InitCondVar(&mon->waitCV, &mon->lock);
    79     PR_ASSERT(rv == PR_SUCCESS);
    80     if (rv != PR_SUCCESS)
    81         goto error3;
    83     mon->notifyTimes = 0;
    84     mon->entryCount = 0;
    85     mon->name = NULL;
    86     return mon;
    88 error3:
    89     _PR_FreeCondVar(&mon->entryCV);
    90 error2:
    91     _PR_FreeLock(&mon->lock);
    92 error1:
    93     PR_Free(mon);
    94     return NULL;
    95 }
    97 PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name)
    98 {
    99     PRMonitor* mon = PR_NewMonitor();
   100     if (mon)
   101         mon->name = name;
   102     return mon;
   103 }
   105 /*
   106 ** Destroy a monitor. There must be no thread waiting on the monitor's
   107 ** condition variable. The caller is responsible for guaranteeing that the
   108 ** monitor is no longer in use.
   109 */
   110 PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon)
   111 {
   112     PR_ASSERT(mon != NULL);
   113     _PR_FreeCondVar(&mon->waitCV);
   114     _PR_FreeCondVar(&mon->entryCV);
   115     _PR_FreeLock(&mon->lock);
   116 #if defined(DEBUG)
   117     memset(mon, 0xaf, sizeof(PRMonitor));
   118 #endif
   119     PR_Free(mon);
   120 }
   122 /*
   123 ** Enter the lock associated with the monitor.
   124 */
   125 PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon)
   126 {
   127     PRThread *me = _PR_MD_CURRENT_THREAD();
   128     PRStatus rv;
   130     PR_ASSERT(mon != NULL);
   131     PR_Lock(&mon->lock);
   132     if (mon->entryCount != 0) {
   133         if (mon->owner == me)
   134             goto done;
   135         while (mon->entryCount != 0) {
   136             rv = PR_WaitCondVar(&mon->entryCV, PR_INTERVAL_NO_TIMEOUT);
   137             PR_ASSERT(rv == PR_SUCCESS);
   138         }
   139     }
   140     /* and now I have the monitor */
   141     PR_ASSERT(mon->notifyTimes == 0);
   142     PR_ASSERT(mon->owner == NULL);
   143     mon->owner = me;
   145 done:
   146     mon->entryCount += 1;
   147     rv = PR_Unlock(&mon->lock);
   148     PR_ASSERT(rv == PR_SUCCESS);
   149 }
   151 /*
   152 ** Test and then enter the lock associated with the monitor if it's not
   153 ** already entered by some other thread. Return PR_FALSE if some other
   154 ** thread owned the lock at the time of the call.
   155 */
   156 PR_IMPLEMENT(PRBool) PR_TestAndEnterMonitor(PRMonitor *mon)
   157 {
   158     PRThread *me = _PR_MD_CURRENT_THREAD();
   159     PRStatus rv;
   161     PR_ASSERT(mon != NULL);
   162     PR_Lock(&mon->lock);
   163     if (mon->entryCount != 0) {
   164         if (mon->owner == me)
   165             goto done;
   166         rv = PR_Unlock(&mon->lock);
   167         PR_ASSERT(rv == PR_SUCCESS);
   168         return PR_FALSE;
   169     }
   170     /* and now I have the monitor */
   171     PR_ASSERT(mon->notifyTimes == 0);
   172     PR_ASSERT(mon->owner == NULL);
   173     mon->owner = me;
   175 done:
   176     mon->entryCount += 1;
   177     rv = PR_Unlock(&mon->lock);
   178     PR_ASSERT(rv == PR_SUCCESS);
   179     return PR_TRUE;
   180 }
   182 /*
   183 ** Exit the lock associated with the monitor once.
   184 */
   185 PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon)
   186 {
   187     PRThread *me = _PR_MD_CURRENT_THREAD();
   188     PRStatus rv;
   190     PR_ASSERT(mon != NULL);
   191     PR_Lock(&mon->lock);
   192     /* the entries should be > 0 and we'd better be the owner */
   193     PR_ASSERT(mon->entryCount > 0);
   194     PR_ASSERT(mon->owner == me);
   195     if (mon->entryCount == 0 || mon->owner != me)
   196     {
   197         rv = PR_Unlock(&mon->lock);
   198         PR_ASSERT(rv == PR_SUCCESS);
   199         return PR_FAILURE;
   200     }
   202     mon->entryCount -= 1;  /* reduce by one */
   203     if (mon->entryCount == 0)
   204     {
   205         /* and if it transitioned to zero - notify an entry waiter */
   206         /* make the owner unknown */
   207         mon->owner = NULL;
   208         if (mon->notifyTimes != 0) {
   209             _PR_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes);
   210             mon->notifyTimes = 0;
   211         }
   212         rv = PR_NotifyCondVar(&mon->entryCV);
   213         PR_ASSERT(rv == PR_SUCCESS);
   214     }
   215     rv = PR_Unlock(&mon->lock);
   216     PR_ASSERT(rv == PR_SUCCESS);
   217     return PR_SUCCESS;
   218 }
   220 /*
   221 ** Return the number of times that the current thread has entered the
   222 ** lock. Returns zero if the current thread has not entered the lock.
   223 */
   224 PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon)
   225 {
   226     PRThread *me = _PR_MD_CURRENT_THREAD();
   227     PRStatus rv;
   228     PRIntn count = 0;
   230     PR_Lock(&mon->lock);
   231     if (mon->owner == me)
   232         count = mon->entryCount;
   233     rv = PR_Unlock(&mon->lock);
   234     PR_ASSERT(rv == PR_SUCCESS);
   235     return count;
   236 }
   238 PR_IMPLEMENT(void) PR_AssertCurrentThreadInMonitor(PRMonitor *mon)
   239 {
   240 #if defined(DEBUG) || defined(FORCE_PR_ASSERT)
   241     PRStatus rv;
   243     PR_Lock(&mon->lock);
   244     PR_ASSERT(mon->entryCount != 0 &&
   245               mon->owner == _PR_MD_CURRENT_THREAD());
   246     rv = PR_Unlock(&mon->lock);
   247     PR_ASSERT(rv == PR_SUCCESS);
   248 #endif
   249 }
   251 /*
   252 ** Wait for a notify on the condition variable. Sleep for "ticks" amount
   253 ** of time (if "tick" is 0 then the sleep is indefinite). While
   254 ** the thread is waiting it exits the monitors lock (as if it called
   255 ** PR_ExitMonitor as many times as it had called PR_EnterMonitor).  When
   256 ** the wait has finished the thread regains control of the monitors lock
   257 ** with the same entry count as before the wait began.
   258 **
   259 ** The thread waiting on the monitor will be resumed when the monitor is
   260 ** notified (assuming the thread is the next in line to receive the
   261 ** notify) or when the "ticks" elapses.
   262 **
   263 ** Returns PR_FAILURE if the caller has not locked the lock associated
   264 ** with the condition variable.
   265 ** This routine can return PR_PENDING_INTERRUPT_ERROR if the waiting thread
   266 ** has been interrupted.
   267 */
   268 PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime ticks)
   269 {
   270     PRStatus rv;
   271     PRUint32 saved_entries;
   272     PRThread *saved_owner;
   274     PR_ASSERT(mon != NULL);
   275     PR_Lock(&mon->lock);
   276     /* the entries better be positive */
   277     PR_ASSERT(mon->entryCount > 0);
   278     /* and it better be owned by us */
   279     PR_ASSERT(mon->owner == _PR_MD_CURRENT_THREAD());  /* XXX return failure */
   281     /* tuck these away 'till later */
   282     saved_entries = mon->entryCount;
   283     mon->entryCount = 0;
   284     saved_owner = mon->owner;
   285     mon->owner = NULL;
   286     /* If we have pending notifies, post them now. */
   287     if (mon->notifyTimes != 0) {
   288         _PR_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes);
   289         mon->notifyTimes = 0;
   290     }
   291     rv = PR_NotifyCondVar(&mon->entryCV);
   292     PR_ASSERT(rv == PR_SUCCESS);
   294     rv = PR_WaitCondVar(&mon->waitCV, ticks);
   295     PR_ASSERT(rv == PR_SUCCESS);
   297     while (mon->entryCount != 0) {
   298         rv = PR_WaitCondVar(&mon->entryCV, PR_INTERVAL_NO_TIMEOUT);
   299         PR_ASSERT(rv == PR_SUCCESS);
   300     }
   301     PR_ASSERT(mon->notifyTimes == 0);
   302     /* reinstate the interesting information */
   303     mon->entryCount = saved_entries;
   304     mon->owner = saved_owner;
   306     rv = PR_Unlock(&mon->lock);
   307     PR_ASSERT(rv == PR_SUCCESS);
   308     return rv;
   309 }
   311 /*
   312 ** Notify the highest priority thread waiting on the condition
   313 ** variable. If a thread is waiting on the condition variable (using
   314 ** PR_Wait) then it is awakened and begins waiting on the monitor's lock.
   315 */
   316 PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon)
   317 {
   318     _PR_PostNotifyToMonitor(mon, PR_FALSE);
   319     return PR_SUCCESS;
   320 }
   322 /*
   323 ** Notify all of the threads waiting on the condition variable. All of
   324 ** threads are notified in turn. The highest priority thread will
   325 ** probably acquire the monitor first when the monitor is exited.
   326 */
   327 PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon)
   328 {
   329     _PR_PostNotifyToMonitor(mon, PR_TRUE);
   330     return PR_SUCCESS;
   331 }
   333 /************************************************************************/
   335 PRUint32 _PR_MonitorToString(PRMonitor *mon, char *buf, PRUint32 buflen)
   336 {
   337     PRUint32 nb;
   339     if (mon->owner) {
   340 	nb = PR_snprintf(buf, buflen, "[%p] owner=%d[%p] count=%ld",
   341 			 mon, mon->owner->id, mon->owner, mon->entryCount);
   342     } else {
   343 	nb = PR_snprintf(buf, buflen, "[%p]", mon);
   344     }
   345     return nb;
   346 }

mercurial