home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / nsprpub / pr / src / md / os2 / os2cv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  10.5 KB  |  333 lines

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