home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / nsprpub / pr / src / md / windows / w95cv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  10.4 KB  |  329 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.  *  w95cv.c -- Windows 95 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.     ZeroMemory(&lock->notified, 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.  
  165.                 rv = ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
  166.                 PR_ASSERT(rv != 0);
  167.                 thred = next;
  168.             }
  169.         }
  170.         prev = notified;
  171.         notified = notified->link;
  172.         if (&post != prev) PR_DELETE(prev);
  173.     } while (NULL != notified);
  174. }
  175.  
  176. /*
  177.  * Notifies just get posted to the protecting mutex.  The
  178.  * actual notification is done when the lock is released so that
  179.  * MP systems don't contend for a lock that they can't have.
  180.  */
  181. static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock,
  182.         PRBool broadcast)
  183. {
  184.     PRIntn index = 0;
  185.     _MDNotified *notified = &lock->notified;
  186.  
  187.     while (1) {
  188.         for (index = 0; index < notified->length; ++index) {
  189.             if (notified->cv[index].cv == cvar) {
  190.                 if (broadcast) {
  191.                     notified->cv[index].times = -1;
  192.                 } else if (-1 != notified->cv[index].times) {
  193.                     notified->cv[index].times += 1;
  194.                 }
  195.                 return;
  196.             }
  197.         }
  198.         /* if not full, enter new CV in this array */
  199.         if (notified->length < _MD_CV_NOTIFIED_LENGTH) break;
  200.  
  201.         /* if there's no link, create an empty array and link it */
  202.         if (NULL == notified->link) {
  203.             notified->link = PR_NEWZAP(_MDNotified);
  204.         }
  205.  
  206.         notified = notified->link;
  207.     }
  208.  
  209.     /* A brand new entry in the array */
  210.     notified->cv[index].times = (broadcast) ? -1 : 1;
  211.     notified->cv[index].cv = cvar;
  212.     notified->length += 1;
  213. }
  214.  
  215. /*
  216.  * _PR_MD_NEW_CV() -- Creating new condition variable
  217.  * ... Solaris uses cond_init() in similar function.
  218.  *
  219.  * returns: -1 on failure
  220.  *          0 when it succeeds.
  221.  *
  222.  */
  223. PRInt32 
  224. _PR_MD_NEW_CV(_MDCVar *cv)
  225. {
  226.     cv->magic = _MD_MAGIC_CV;
  227.     /*
  228.      * The waitHead, waitTail, and nwait fields are zeroed
  229.      * when the PRCondVar structure is created.
  230.      */
  231.     return 0;
  232.  
  233. void _PR_MD_FREE_CV(_MDCVar *cv)
  234. {
  235.     cv->magic = (PRUint32)-1;
  236.     return;
  237. }
  238.  
  239. /*
  240.  *  _PR_MD_WAIT_CV() -- Wait on condition variable
  241.  */
  242. void _PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout )
  243. {
  244.     PRThread *thred = _PR_MD_CURRENT_THREAD();
  245.     DWORD rv;
  246.     DWORD msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ?
  247.             INFINITE : 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 = WaitForSingleObject(thred->md.blocked_sema, msecs);
  261.  
  262.     EnterCriticalSection(&(lock->mutex));
  263.  
  264.     PR_ASSERT(rv != WAIT_ABANDONED);
  265.     PR_ASSERT(rv != WAIT_FAILED);
  266.     PR_ASSERT(rv != WAIT_OBJECT_0 || thred->md.inCVWaitQueue == PR_FALSE);
  267.  
  268.     if (rv == WAIT_TIMEOUT) {
  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.              * ReleaseSemaphore call happens after WaitForSingleObject
  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 = WaitForSingleObject(thred->md.blocked_sema, INFINITE);
  300.             PR_ASSERT(rv == WAIT_OBJECT_0);
  301.         }
  302.     }
  303.     PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE);
  304.     return;
  305. } /* --- end _PR_MD_WAIT_CV() --- */
  306.  
  307. void _PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock)
  308. {
  309.     md_PostNotifyToCvar(cv, lock, PR_FALSE);
  310.     return;
  311. }
  312.  
  313. void _PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock)
  314. {
  315.     md_PostNotifyToCvar(cv, lock, PR_TRUE);
  316.     return;
  317. }
  318.  
  319. void _PR_MD_UNLOCK(_MDLock *lock)
  320. {
  321.     if (0 != lock->notified.length) {
  322.         md_UnlockAndPostNotifies(lock, NULL, NULL);
  323.     } else {
  324.         LeaveCriticalSection(&lock->mutex);
  325.     }
  326.     return;
  327. }
  328.