nsprpub/pr/src/md/os2/os2cv.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  *  os2cv.c -- OS/2 Machine-Dependent Code for Condition Variables
     8  *
     9  *  We implement our own condition variable wait queue.  Each thread
    10  *  has a semaphore object (thread->md.blocked_sema) to block on while
    11  *  waiting on a condition variable.
    12  *
    13  *  We use a deferred condition notify algorithm.  When PR_NotifyCondVar
    14  *  or PR_NotifyAllCondVar is called, the condition notifies are simply
    15  *  recorded in the _MDLock structure.  We defer the condition notifies
    16  *  until right after we unlock the lock.  This way the awakened threads
    17  *  have a better chance to reaquire the lock.
    18  */
    20 #include "primpl.h"
    22 /*
    23  * AddThreadToCVWaitQueueInternal --
    24  *
    25  * Add the thread to the end of the condition variable's wait queue.
    26  * The CV's lock must be locked when this function is called.
    27  */
    29 static void
    30 AddThreadToCVWaitQueueInternal(PRThread *thred, struct _MDCVar *cv)
    31 {
    32     PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
    33             || (cv->waitTail == NULL && cv->waitHead == NULL));
    34     cv->nwait += 1;
    35     thred->md.inCVWaitQueue = PR_TRUE;
    36     thred->md.next = NULL;
    37     thred->md.prev = cv->waitTail;
    38     if (cv->waitHead == NULL) {
    39         cv->waitHead = thred;
    40     } else {
    41         cv->waitTail->md.next = thred;
    42     }
    43     cv->waitTail = thred;
    44 }
    46 /*
    47  * md_UnlockAndPostNotifies --
    48  *
    49  * Unlock the lock, and then do the deferred condition notifies.
    50  * If waitThred and waitCV are not NULL, waitThred is added to
    51  * the wait queue of waitCV before the lock is unlocked.
    52  *
    53  * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK,
    54  * the two places where a lock is unlocked.
    55  */
    56 void
    57 md_UnlockAndPostNotifies(
    58     _MDLock *lock,
    59     PRThread *waitThred,
    60     _MDCVar *waitCV)
    61 {
    62     PRIntn index;
    63     _MDNotified post;
    64     _MDNotified *notified, *prev = NULL;
    66     /*
    67      * Time to actually notify any conditions that were affected
    68      * while the lock was held.  Get a copy of the list that's in
    69      * the lock structure and then zero the original.  If it's
    70      * linked to other such structures, we own that storage.
    71      */
    72     post = lock->notified;  /* a safe copy; we own the lock */
    74 #if defined(DEBUG)
    75     memset(&lock->notified, 0, sizeof(_MDNotified));  /* reset */
    76 #else
    77     lock->notified.length = 0;  /* these are really sufficient */
    78     lock->notified.link = NULL;
    79 #endif
    81     /* 
    82      * Figure out how many threads we need to wake up.
    83      */
    84     notified = &post;  /* this is where we start */
    85     do {
    86         for (index = 0; index < notified->length; ++index) {
    87             _MDCVar *cv = notified->cv[index].cv;
    88             PRThread *thred;
    89             int i;
    91             /* Fast special case: no waiting threads */
    92             if (cv->waitHead == NULL) {
    93                 notified->cv[index].notifyHead = NULL;
    94                 continue;
    95             }
    97             /* General case */
    98             if (-1 == notified->cv[index].times) {
    99                 /* broadcast */
   100                 thred = cv->waitHead;
   101                 while (thred != NULL) {
   102                     thred->md.inCVWaitQueue = PR_FALSE;
   103                     thred = thred->md.next;
   104                 }
   105                 notified->cv[index].notifyHead = cv->waitHead;
   106                 cv->waitHead = cv->waitTail = NULL;
   107                 cv->nwait = 0;
   108             } else {
   109                 thred = cv->waitHead;
   110                 i = notified->cv[index].times;
   111                 while (thred != NULL && i > 0) {
   112                     thred->md.inCVWaitQueue = PR_FALSE;
   113                     thred = thred->md.next;
   114                     i--;
   115                 }
   116                 notified->cv[index].notifyHead = cv->waitHead;
   117                 cv->waitHead = thred;
   118                 if (cv->waitHead == NULL) {
   119                     cv->waitTail = NULL;
   120                 } else {
   121                     if (cv->waitHead->md.prev != NULL) {
   122                         cv->waitHead->md.prev->md.next = NULL;
   123                         cv->waitHead->md.prev = NULL;
   124                     }
   125                 }
   126                 cv->nwait -= notified->cv[index].times - i;
   127             }
   128         }
   129         notified = notified->link;
   130     } while (NULL != notified);
   132     if (waitThred) {
   133         AddThreadToCVWaitQueueInternal(waitThred, waitCV);
   134     }
   136     /* Release the lock before notifying */
   137     DosReleaseMutexSem(lock->mutex);
   139     notified = &post;  /* this is where we start */
   140     do {
   141         for (index = 0; index < notified->length; ++index) {
   142             PRThread *thred;
   143             PRThread *next;
   145             thred = notified->cv[index].notifyHead;
   146             while (thred != NULL) {
   147                 BOOL rv;
   149                 next = thred->md.next;
   150                 thred->md.prev = thred->md.next = NULL;
   151                 rv = DosPostEventSem(thred->md.blocked_sema);
   152                 PR_ASSERT(rv == NO_ERROR);
   153                 thred = next;
   154             }
   155         }
   156         prev = notified;
   157         notified = notified->link;
   158         if (&post != prev) PR_DELETE(prev);
   159     } while (NULL != notified);
   160 }
   162 /*
   163  * Notifies just get posted to the protecting mutex.  The
   164  * actual notification is done when the lock is released so that
   165  * MP systems don't contend for a lock that they can't have.
   166  */
   167 static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock,
   168         PRBool broadcast)
   169 {
   170     PRIntn index = 0;
   171     _MDNotified *notified = &lock->notified;
   173     while (1) {
   174         for (index = 0; index < notified->length; ++index) {
   175             if (notified->cv[index].cv == cvar) {
   176                 if (broadcast) {
   177                     notified->cv[index].times = -1;
   178                 } else if (-1 != notified->cv[index].times) {
   179                     notified->cv[index].times += 1;
   180                 }
   181                 return;
   182             }
   183         }
   184         /* if not full, enter new CV in this array */
   185         if (notified->length < _MD_CV_NOTIFIED_LENGTH) break;
   187         /* if there's no link, create an empty array and link it */
   188         if (NULL == notified->link) {
   189             notified->link = PR_NEWZAP(_MDNotified);
   190         }
   192         notified = notified->link;
   193     }
   195     /* A brand new entry in the array */
   196     notified->cv[index].times = (broadcast) ? -1 : 1;
   197     notified->cv[index].cv = cvar;
   198     notified->length += 1;
   199 }
   201 /*
   202  * _PR_MD_NEW_CV() -- Creating new condition variable
   203  * ... Solaris uses cond_init() in similar function.
   204  *
   205  * returns: -1 on failure
   206  *          0 when it succeeds.
   207  *
   208  */
   209 PRInt32
   210 _PR_MD_NEW_CV(_MDCVar *cv)
   211 {
   212     cv->magic = _MD_MAGIC_CV;
   213     /*
   214      * The waitHead, waitTail, and nwait fields are zeroed
   215      * when the PRCondVar structure is created.
   216      */
   217     return 0;
   218 } 
   220 void _PR_MD_FREE_CV(_MDCVar *cv)
   221 {
   222     cv->magic = (PRUint32)-1;
   223     return;
   224 }
   226 /*
   227  *  _PR_MD_WAIT_CV() -- Wait on condition variable
   228  */
   229 void
   230 _PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout )
   231 {
   232     PRThread *thred = _PR_MD_CURRENT_THREAD();
   233     ULONG rv, count;
   234     ULONG msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ?
   235             SEM_INDEFINITE_WAIT : PR_IntervalToMilliseconds(timeout);
   237     /*
   238      * If we have pending notifies, post them now.
   239      */
   240     if (0 != lock->notified.length) {
   241         md_UnlockAndPostNotifies(lock, thred, cv);
   242     } else {
   243         AddThreadToCVWaitQueueInternal(thred, cv);
   244         DosReleaseMutexSem(lock->mutex); 
   245     }
   247     /* Wait for notification or timeout; don't really care which */
   248     rv = DosWaitEventSem(thred->md.blocked_sema, msecs);
   249     if (rv == NO_ERROR) {
   250         DosResetEventSem(thred->md.blocked_sema, &count);
   251     }
   253     DosRequestMutexSem((lock->mutex), SEM_INDEFINITE_WAIT);
   255     PR_ASSERT(rv == NO_ERROR || rv == ERROR_TIMEOUT);
   257     if(rv == ERROR_TIMEOUT)
   258     {
   259        if (thred->md.inCVWaitQueue) {
   260            PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
   261                    || (cv->waitTail == NULL && cv->waitHead == NULL));
   262            cv->nwait -= 1;
   263            thred->md.inCVWaitQueue = PR_FALSE;
   264            if (cv->waitHead == thred) {
   265                cv->waitHead = thred->md.next;
   266                if (cv->waitHead == NULL) {
   267                    cv->waitTail = NULL;
   268                } else {
   269                    cv->waitHead->md.prev = NULL;
   270                }
   271            } else {
   272                PR_ASSERT(thred->md.prev != NULL);
   273                thred->md.prev->md.next = thred->md.next;
   274                if (thred->md.next != NULL) {
   275                    thred->md.next->md.prev = thred->md.prev;
   276                } else {
   277                    PR_ASSERT(cv->waitTail == thred);
   278                    cv->waitTail = thred->md.prev;
   279                }
   280            }
   281            thred->md.next = thred->md.prev = NULL;
   282        } else {
   283            /*
   284             * This thread must have been notified, but the
   285             * SemRelease call happens after SemRequest
   286             * times out.  Wait on the semaphore again to make it
   287             * non-signaled.  We assume this wait won't take long.
   288             */
   289            rv = DosWaitEventSem(thred->md.blocked_sema, SEM_INDEFINITE_WAIT);
   290            if (rv == NO_ERROR) {
   291                DosResetEventSem(thred->md.blocked_sema, &count);
   292            }
   293            PR_ASSERT(rv == NO_ERROR);
   294        }
   295     }
   296     PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE);
   297     return;
   298 } /* --- end _PR_MD_WAIT_CV() --- */
   300 void
   301 _PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock)
   302 {
   303     md_PostNotifyToCvar(cv, lock, PR_FALSE);
   304     return;
   305 }
   307 PRStatus
   308 _PR_MD_NEW_LOCK(_MDLock *lock)
   309 {
   310     DosCreateMutexSem(0, &(lock->mutex), 0, 0);
   311     (lock)->notified.length=0;
   312     (lock)->notified.link=NULL;
   313     return PR_SUCCESS;
   314 }
   316 void
   317 _PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock)
   318 {
   319     md_PostNotifyToCvar(cv, lock, PR_TRUE);
   320     return;
   321 }
   323 void _PR_MD_UNLOCK(_MDLock *lock)
   324 {
   325     if (0 != lock->notified.length) {
   326         md_UnlockAndPostNotifies(lock, NULL, NULL);
   327     } else {
   328         DosReleaseMutexSem(lock->mutex);
   329     }
   330 }

mercurial