Back to index

lightning-sunbird  0.9+nobinonly
w95cv.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039  *  w95cv.c -- Windows 95 Machine-Dependent Code for Condition Variables
00040  *
00041  *  We implement our own condition variable wait queue.  Each thread
00042  *  has a semaphore object (thread->md.blocked_sema) to block on while
00043  *  waiting on a condition variable.
00044  *
00045  *  We use a deferred condition notify algorithm.  When PR_NotifyCondVar
00046  *  or PR_NotifyAllCondVar is called, the condition notifies are simply
00047  *  recorded in the _MDLock structure.  We defer the condition notifies
00048  *  until right after we unlock the lock.  This way the awakened threads
00049  *  have a better chance to reaquire the lock.
00050  */
00051  
00052 #include "primpl.h"
00053 
00054 /*
00055  * AddThreadToCVWaitQueueInternal --
00056  *
00057  * Add the thread to the end of the condition variable's wait queue.
00058  * The CV's lock must be locked when this function is called.
00059  */
00060 
00061 static void
00062 AddThreadToCVWaitQueueInternal(PRThread *thred, struct _MDCVar *cv)
00063 {
00064     PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
00065             || (cv->waitTail == NULL && cv->waitHead == NULL));
00066     cv->nwait += 1;
00067     thred->md.inCVWaitQueue = PR_TRUE;
00068     thred->md.next = NULL;
00069     thred->md.prev = cv->waitTail;
00070     if (cv->waitHead == NULL) {
00071         cv->waitHead = thred;
00072     } else {
00073         cv->waitTail->md.next = thred;
00074     }
00075     cv->waitTail = thred;
00076 }
00077 
00078 /*
00079  * md_UnlockAndPostNotifies --
00080  *
00081  * Unlock the lock, and then do the deferred condition notifies.
00082  * If waitThred and waitCV are not NULL, waitThred is added to
00083  * the wait queue of waitCV before the lock is unlocked.
00084  *
00085  * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK,
00086  * the two places where a lock is unlocked.
00087  */
00088 static void
00089 md_UnlockAndPostNotifies(
00090     _MDLock *lock,
00091     PRThread *waitThred,
00092     _MDCVar *waitCV)
00093 {
00094     PRIntn index;
00095     _MDNotified post;
00096     _MDNotified *notified, *prev = NULL;
00097 
00098     /*
00099      * Time to actually notify any conditions that were affected
00100      * while the lock was held.  Get a copy of the list that's in
00101      * the lock structure and then zero the original.  If it's
00102      * linked to other such structures, we own that storage.
00103      */
00104     post = lock->notified;  /* a safe copy; we own the lock */
00105 
00106 #if defined(DEBUG)
00107     ZeroMemory(&lock->notified, sizeof(_MDNotified));  /* reset */
00108 #else
00109     lock->notified.length = 0;  /* these are really sufficient */
00110     lock->notified.link = NULL;
00111 #endif
00112 
00113     /* 
00114      * Figure out how many threads we need to wake up.
00115      */
00116     notified = &post;  /* this is where we start */
00117     do {
00118         for (index = 0; index < notified->length; ++index) {
00119             _MDCVar *cv = notified->cv[index].cv;
00120             PRThread *thred;
00121             int i;
00122             
00123             /* Fast special case: no waiting threads */
00124             if (cv->waitHead == NULL) {
00125                 notified->cv[index].notifyHead = NULL;
00126                 continue;
00127             }
00128 
00129             /* General case */
00130             if (-1 == notified->cv[index].times) {
00131                 /* broadcast */
00132                 thred = cv->waitHead;
00133                 while (thred != NULL) {
00134                     thred->md.inCVWaitQueue = PR_FALSE;
00135                     thred = thred->md.next;
00136                 }
00137                 notified->cv[index].notifyHead = cv->waitHead;
00138                 cv->waitHead = cv->waitTail = NULL;
00139                 cv->nwait = 0;
00140             } else {
00141                 thred = cv->waitHead;
00142                 i = notified->cv[index].times;
00143                 while (thred != NULL && i > 0) {
00144                     thred->md.inCVWaitQueue = PR_FALSE;
00145                     thred = thred->md.next;
00146                     i--;
00147                 }
00148                 notified->cv[index].notifyHead = cv->waitHead;
00149                 cv->waitHead = thred;
00150                 if (cv->waitHead == NULL) {
00151                     cv->waitTail = NULL;
00152                 } else {
00153                     if (cv->waitHead->md.prev != NULL) {
00154                         cv->waitHead->md.prev->md.next = NULL;
00155                         cv->waitHead->md.prev = NULL;
00156                     }
00157                 }
00158                 cv->nwait -= notified->cv[index].times - i;
00159             }
00160         }
00161         notified = notified->link;
00162     } while (NULL != notified);
00163 
00164     if (waitThred) {
00165         AddThreadToCVWaitQueueInternal(waitThred, waitCV);
00166     }
00167 
00168     /* Release the lock before notifying */
00169         LeaveCriticalSection(&lock->mutex);
00170 
00171     notified = &post;  /* this is where we start */
00172     do {
00173         for (index = 0; index < notified->length; ++index) {
00174             PRThread *thred;
00175             PRThread *next;
00176 
00177             thred = notified->cv[index].notifyHead;
00178             while (thred != NULL) {
00179                 BOOL rv;
00180 
00181                 next = thred->md.next;
00182                 thred->md.prev = thred->md.next = NULL;
00183 
00184                 rv = ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
00185                 PR_ASSERT(rv != 0);
00186                 thred = next;
00187             }
00188         }
00189         prev = notified;
00190         notified = notified->link;
00191         if (&post != prev) PR_DELETE(prev);
00192     } while (NULL != notified);
00193 }
00194 
00195 /*
00196  * Notifies just get posted to the protecting mutex.  The
00197  * actual notification is done when the lock is released so that
00198  * MP systems don't contend for a lock that they can't have.
00199  */
00200 static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock,
00201         PRBool broadcast)
00202 {
00203     PRIntn index = 0;
00204     _MDNotified *notified = &lock->notified;
00205 
00206     while (1) {
00207         for (index = 0; index < notified->length; ++index) {
00208             if (notified->cv[index].cv == cvar) {
00209                 if (broadcast) {
00210                     notified->cv[index].times = -1;
00211                 } else if (-1 != notified->cv[index].times) {
00212                     notified->cv[index].times += 1;
00213                 }
00214                 return;
00215             }
00216         }
00217         /* if not full, enter new CV in this array */
00218         if (notified->length < _MD_CV_NOTIFIED_LENGTH) break;
00219 
00220         /* if there's no link, create an empty array and link it */
00221         if (NULL == notified->link) {
00222             notified->link = PR_NEWZAP(_MDNotified);
00223         }
00224 
00225         notified = notified->link;
00226     }
00227 
00228     /* A brand new entry in the array */
00229     notified->cv[index].times = (broadcast) ? -1 : 1;
00230     notified->cv[index].cv = cvar;
00231     notified->length += 1;
00232 }
00233 
00234 /*
00235  * _PR_MD_NEW_CV() -- Creating new condition variable
00236  * ... Solaris uses cond_init() in similar function.
00237  *
00238  * returns: -1 on failure
00239  *          0 when it succeeds.
00240  *
00241  */
00242 PRInt32 
00243 _PR_MD_NEW_CV(_MDCVar *cv)
00244 {
00245     cv->magic = _MD_MAGIC_CV;
00246     /*
00247      * The waitHead, waitTail, and nwait fields are zeroed
00248      * when the PRCondVar structure is created.
00249      */
00250     return 0;
00251 } 
00252 
00253 void _PR_MD_FREE_CV(_MDCVar *cv)
00254 {
00255     cv->magic = (PRUint32)-1;
00256     return;
00257 }
00258 
00259 /*
00260  *  _PR_MD_WAIT_CV() -- Wait on condition variable
00261  */
00262 void _PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout )
00263 {
00264     PRThread *thred = _PR_MD_CURRENT_THREAD();
00265     DWORD rv;
00266     DWORD msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ?
00267             INFINITE : PR_IntervalToMilliseconds(timeout);
00268 
00269     /*
00270      * If we have pending notifies, post them now.
00271      */
00272     if (0 != lock->notified.length) {
00273         md_UnlockAndPostNotifies(lock, thred, cv);
00274     } else {
00275         AddThreadToCVWaitQueueInternal(thred, cv);
00276         LeaveCriticalSection(&lock->mutex);
00277     }
00278 
00279     /* Wait for notification or timeout; don't really care which */
00280     rv = WaitForSingleObject(thred->md.blocked_sema, msecs);
00281 
00282     EnterCriticalSection(&(lock->mutex));
00283 
00284     PR_ASSERT(rv != WAIT_ABANDONED);
00285     PR_ASSERT(rv != WAIT_FAILED);
00286     PR_ASSERT(rv != WAIT_OBJECT_0 || thred->md.inCVWaitQueue == PR_FALSE);
00287 
00288     if (rv == WAIT_TIMEOUT) {
00289         if (thred->md.inCVWaitQueue) {
00290             PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
00291                     || (cv->waitTail == NULL && cv->waitHead == NULL));
00292             cv->nwait -= 1;
00293             thred->md.inCVWaitQueue = PR_FALSE;
00294             if (cv->waitHead == thred) {
00295                 cv->waitHead = thred->md.next;
00296                 if (cv->waitHead == NULL) {
00297                     cv->waitTail = NULL;
00298                 } else {
00299                     cv->waitHead->md.prev = NULL;
00300                 }
00301             } else {
00302                 PR_ASSERT(thred->md.prev != NULL);
00303                 thred->md.prev->md.next = thred->md.next;
00304                 if (thred->md.next != NULL) {
00305                     thred->md.next->md.prev = thred->md.prev;
00306                 } else {
00307                     PR_ASSERT(cv->waitTail == thred);
00308                     cv->waitTail = thred->md.prev;
00309                 }
00310             }
00311             thred->md.next = thred->md.prev = NULL;
00312         } else {
00313             /*
00314              * This thread must have been notified, but the
00315              * ReleaseSemaphore call happens after WaitForSingleObject
00316              * times out.  Wait on the semaphore again to make it
00317              * non-signaled.  We assume this wait won't take long.
00318              */
00319             rv = WaitForSingleObject(thred->md.blocked_sema, INFINITE);
00320             PR_ASSERT(rv == WAIT_OBJECT_0);
00321         }
00322     }
00323     PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE);
00324     return;
00325 } /* --- end _PR_MD_WAIT_CV() --- */
00326 
00327 void _PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock)
00328 {
00329     md_PostNotifyToCvar(cv, lock, PR_FALSE);
00330     return;
00331 }
00332 
00333 void _PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock)
00334 {
00335     md_PostNotifyToCvar(cv, lock, PR_TRUE);
00336     return;
00337 }
00338 
00339 void _PR_MD_UNLOCK(_MDLock *lock)
00340 {
00341     if (0 != lock->notified.length) {
00342         md_UnlockAndPostNotifies(lock, NULL, NULL);
00343     } else {
00344         LeaveCriticalSection(&lock->mutex);
00345     }
00346     return;
00347 }