Back to index

lightning-sunbird  0.9+nobinonly
os2cv.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  *  os2cv.c -- OS/2 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 #ifdef USE_RAMSEM
00055 ULONG _Far16 _Pascal Dos16GetInfoSeg(PSEL pselGlobal, PSEL pselLocal);
00056 
00057 #ifdef XP_OS2_EMX
00058 typedef unsigned short BOOL16;
00059 #endif
00060 
00061 typedef struct _LINFOSEG
00062 {
00063     USHORT  pidCurrent;
00064     USHORT  pidParent;
00065     USHORT  prtyCurrent;
00066     USHORT  tidCurrent;
00067     USHORT  sgCurrent;
00068     UCHAR   rfProcStatus;
00069     UCHAR   dummy1;
00070     BOOL16  fForeground;
00071     UCHAR   typProcess;
00072     UCHAR   dummy2;
00073     SEL     selEnvironment;
00074     USHORT  offCmdLine;
00075     USHORT  cbDataSegment;
00076     USHORT  cbStack;
00077     USHORT  cbHeap;
00078     USHORT  hmod;
00079     SEL     selDS;
00080     SEL     selPack;
00081     SEL     selPackShr;
00082     SEL     selPackPck;
00083     ULONG   ulReserved;
00084 } LINFOSEG;
00085 typedef LINFOSEG FAR *PLINFOSEG;
00086 
00087 PLINFOSEG plisCurrent = NULL;
00088 #endif
00089 
00090 /*
00091  * AddThreadToCVWaitQueueInternal --
00092  *
00093  * Add the thread to the end of the condition variable's wait queue.
00094  * The CV's lock must be locked when this function is called.
00095  */
00096 
00097 static void
00098 AddThreadToCVWaitQueueInternal(PRThread *thred, struct _MDCVar *cv)
00099 {
00100     PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
00101             || (cv->waitTail == NULL && cv->waitHead == NULL));
00102     cv->nwait += 1;
00103     thred->md.inCVWaitQueue = PR_TRUE;
00104     thred->md.next = NULL;
00105     thred->md.prev = cv->waitTail;
00106     if (cv->waitHead == NULL) {
00107         cv->waitHead = thred;
00108     } else {
00109         cv->waitTail->md.next = thred;
00110     }
00111     cv->waitTail = thred;
00112 }
00113 
00114 /*
00115  * md_UnlockAndPostNotifies --
00116  *
00117  * Unlock the lock, and then do the deferred condition notifies.
00118  * If waitThred and waitCV are not NULL, waitThred is added to
00119  * the wait queue of waitCV before the lock is unlocked.
00120  *
00121  * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK,
00122  * the two places where a lock is unlocked.
00123  */
00124 void
00125 md_UnlockAndPostNotifies(
00126     _MDLock *lock,
00127     PRThread *waitThred,
00128     _MDCVar *waitCV)
00129 {
00130     PRIntn index;
00131     _MDNotified post;
00132     _MDNotified *notified, *prev = NULL;
00133 
00134     /*
00135      * Time to actually notify any conditions that were affected
00136      * while the lock was held.  Get a copy of the list that's in
00137      * the lock structure and then zero the original.  If it's
00138      * linked to other such structures, we own that storage.
00139      */
00140     post = lock->notified;  /* a safe copy; we own the lock */
00141 
00142 #if defined(DEBUG)
00143     memset(&lock->notified, 0, sizeof(_MDNotified));  /* reset */
00144 #else
00145     lock->notified.length = 0;  /* these are really sufficient */
00146     lock->notified.link = NULL;
00147 #endif
00148 
00149     /* 
00150      * Figure out how many threads we need to wake up.
00151      */
00152     notified = &post;  /* this is where we start */
00153     do {
00154         for (index = 0; index < notified->length; ++index) {
00155             _MDCVar *cv = notified->cv[index].cv;
00156             PRThread *thred;
00157             int i;
00158             
00159             /* Fast special case: no waiting threads */
00160             if (cv->waitHead == NULL) {
00161                 notified->cv[index].notifyHead = NULL;
00162                 continue;
00163             }
00164 
00165             /* General case */
00166             if (-1 == notified->cv[index].times) {
00167                 /* broadcast */
00168                 thred = cv->waitHead;
00169                 while (thred != NULL) {
00170                     thred->md.inCVWaitQueue = PR_FALSE;
00171                     thred = thred->md.next;
00172                 }
00173                 notified->cv[index].notifyHead = cv->waitHead;
00174                 cv->waitHead = cv->waitTail = NULL;
00175                 cv->nwait = 0;
00176             } else {
00177                 thred = cv->waitHead;
00178                 i = notified->cv[index].times;
00179                 while (thred != NULL && i > 0) {
00180                     thred->md.inCVWaitQueue = PR_FALSE;
00181                     thred = thred->md.next;
00182                     i--;
00183                 }
00184                 notified->cv[index].notifyHead = cv->waitHead;
00185                 cv->waitHead = thred;
00186                 if (cv->waitHead == NULL) {
00187                     cv->waitTail = NULL;
00188                 } else {
00189                     if (cv->waitHead->md.prev != NULL) {
00190                         cv->waitHead->md.prev->md.next = NULL;
00191                         cv->waitHead->md.prev = NULL;
00192                     }
00193                 }
00194                 cv->nwait -= notified->cv[index].times - i;
00195             }
00196         }
00197         notified = notified->link;
00198     } while (NULL != notified);
00199 
00200     if (waitThred) {
00201         AddThreadToCVWaitQueueInternal(waitThred, waitCV);
00202     }
00203 
00204     /* Release the lock before notifying */
00205 #ifdef USE_RAMSEM
00206       SemReleasex86(&lock->mutex, 0);
00207 #else
00208       DosReleaseMutexSem(lock->mutex);
00209 #endif
00210 
00211     notified = &post;  /* this is where we start */
00212     do {
00213         for (index = 0; index < notified->length; ++index) {
00214             PRThread *thred;
00215             PRThread *next;
00216 
00217             thred = notified->cv[index].notifyHead;
00218             while (thred != NULL) {
00219                 BOOL rv;
00220 
00221                 next = thred->md.next;
00222                 thred->md.prev = thred->md.next = NULL;
00223                 rv = DosPostEventSem(thred->md.blocked_sema);
00224                 PR_ASSERT(rv == NO_ERROR);
00225                 thred = next;
00226             }
00227         }
00228         prev = notified;
00229         notified = notified->link;
00230         if (&post != prev) PR_DELETE(prev);
00231     } while (NULL != notified);
00232 }
00233 
00234 /*
00235  * Notifies just get posted to the protecting mutex.  The
00236  * actual notification is done when the lock is released so that
00237  * MP systems don't contend for a lock that they can't have.
00238  */
00239 static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock,
00240         PRBool broadcast)
00241 {
00242     PRIntn index = 0;
00243     _MDNotified *notified = &lock->notified;
00244 
00245     while (1) {
00246         for (index = 0; index < notified->length; ++index) {
00247             if (notified->cv[index].cv == cvar) {
00248                 if (broadcast) {
00249                     notified->cv[index].times = -1;
00250                 } else if (-1 != notified->cv[index].times) {
00251                     notified->cv[index].times += 1;
00252                 }
00253                 return;
00254             }
00255         }
00256         /* if not full, enter new CV in this array */
00257         if (notified->length < _MD_CV_NOTIFIED_LENGTH) break;
00258 
00259         /* if there's no link, create an empty array and link it */
00260         if (NULL == notified->link) {
00261             notified->link = PR_NEWZAP(_MDNotified);
00262         }
00263 
00264         notified = notified->link;
00265     }
00266 
00267     /* A brand new entry in the array */
00268     notified->cv[index].times = (broadcast) ? -1 : 1;
00269     notified->cv[index].cv = cvar;
00270     notified->length += 1;
00271 }
00272 
00273 /*
00274  * _PR_MD_NEW_CV() -- Creating new condition variable
00275  * ... Solaris uses cond_init() in similar function.
00276  *
00277  * returns: -1 on failure
00278  *          0 when it succeeds.
00279  *
00280  */
00281 PRInt32
00282 _PR_MD_NEW_CV(_MDCVar *cv)
00283 {
00284     cv->magic = _MD_MAGIC_CV;
00285     /*
00286      * The waitHead, waitTail, and nwait fields are zeroed
00287      * when the PRCondVar structure is created.
00288      */
00289     return 0;
00290 } 
00291 
00292 void _PR_MD_FREE_CV(_MDCVar *cv)
00293 {
00294     cv->magic = (PRUint32)-1;
00295     return;
00296 }
00297 
00298 /*
00299  *  _PR_MD_WAIT_CV() -- Wait on condition variable
00300  */
00301 void
00302 _PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout )
00303 {
00304     PRThread *thred = _PR_MD_CURRENT_THREAD();
00305     ULONG rv, count;
00306     ULONG msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ?
00307             SEM_INDEFINITE_WAIT : PR_IntervalToMilliseconds(timeout);
00308 
00309     /*
00310      * If we have pending notifies, post them now.
00311      */
00312     if (0 != lock->notified.length) {
00313         md_UnlockAndPostNotifies(lock, thred, cv);
00314     } else {
00315         AddThreadToCVWaitQueueInternal(thred, cv);
00316 #ifdef USE_RAMSEM
00317         SemReleasex86( &lock->mutex, 0 );
00318 #else
00319         DosReleaseMutexSem(lock->mutex); 
00320 #endif
00321     }
00322 
00323     /* Wait for notification or timeout; don't really care which */
00324     rv = DosWaitEventSem(thred->md.blocked_sema, msecs);
00325     if (rv == NO_ERROR) {
00326         DosResetEventSem(thred->md.blocked_sema, &count);
00327     }
00328 
00329 #ifdef USE_RAMSEM
00330     SemRequest486(&(lock->mutex), -1);
00331 #else
00332     DosRequestMutexSem((lock->mutex), SEM_INDEFINITE_WAIT);
00333 #endif
00334 
00335     PR_ASSERT(rv == NO_ERROR || rv == ERROR_TIMEOUT);
00336 
00337     if(rv == ERROR_TIMEOUT)
00338     {
00339        if (thred->md.inCVWaitQueue) {
00340            PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
00341                    || (cv->waitTail == NULL && cv->waitHead == NULL));
00342            cv->nwait -= 1;
00343            thred->md.inCVWaitQueue = PR_FALSE;
00344            if (cv->waitHead == thred) {
00345                cv->waitHead = thred->md.next;
00346                if (cv->waitHead == NULL) {
00347                    cv->waitTail = NULL;
00348                } else {
00349                    cv->waitHead->md.prev = NULL;
00350                }
00351            } else {
00352                PR_ASSERT(thred->md.prev != NULL);
00353                thred->md.prev->md.next = thred->md.next;
00354                if (thred->md.next != NULL) {
00355                    thred->md.next->md.prev = thred->md.prev;
00356                } else {
00357                    PR_ASSERT(cv->waitTail == thred);
00358                    cv->waitTail = thred->md.prev;
00359                }
00360            }
00361            thred->md.next = thred->md.prev = NULL;
00362        } else {
00363            /*
00364             * This thread must have been notified, but the
00365             * SemRelease call happens after SemRequest
00366             * times out.  Wait on the semaphore again to make it
00367             * non-signaled.  We assume this wait won't take long.
00368             */
00369            rv = DosWaitEventSem(thred->md.blocked_sema, SEM_INDEFINITE_WAIT);
00370            if (rv == NO_ERROR) {
00371                DosResetEventSem(thred->md.blocked_sema, &count);
00372            }
00373            PR_ASSERT(rv == NO_ERROR);
00374        }
00375     }
00376     PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE);
00377     return;
00378 } /* --- end _PR_MD_WAIT_CV() --- */
00379 
00380 void
00381 _PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock)
00382 {
00383     md_PostNotifyToCvar(cv, lock, PR_FALSE);
00384     return;
00385 }
00386 
00387 PRStatus
00388 _PR_MD_NEW_LOCK(_MDLock *lock)
00389 {
00390 #ifdef USE_RAMSEM
00391     // It's better if this API traps when pCriticalSect is not a valid
00392     // pointer, because we can't return an error code and if we just return
00393     // the API caller will have nasty bugs that are hard to find.
00394    
00395     PRAMSEM pramsem = (PRAMSEM)(&(lock->mutex));
00396     /* First time, set up addresses of processor specific functions
00397      */
00398     if (plisCurrent == NULL)
00399     {
00400         SEL selGlobal = 0, selLocal = 0;
00401    
00402         /* Convert 16 bit global information segment to 32 bit address
00403          * by performing CRMA on the 16 bit address: "shift" operation
00404          * to convert sel to flat, "and" operation to mask the address
00405          * to 32-bit
00406          */
00407         Dos16GetInfoSeg(&selGlobal, &selLocal);
00408         plisCurrent = (PLINFOSEG)(((ULONG)selLocal << 13) &
00409                       (ULONG)0x1fff0000);
00410    
00411     }
00412    
00413     memset(pramsem, 0, sizeof(pramsem));
00414     DosCreateEventSem(0, &pramsem->hevSem, DC_SEM_SHARED, 0);
00415    
00416     lock->notified.length=0;
00417     lock->notified.link=NULL;
00418     return PR_SUCCESS;
00419 #else
00420     DosCreateMutexSem(0, &(lock->mutex), 0, 0);
00421     (lock)->notified.length=0;
00422     (lock)->notified.link=NULL;
00423     return PR_SUCCESS;
00424 #endif
00425 }
00426 
00427 void
00428 _PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock)
00429 {
00430     md_PostNotifyToCvar(cv, lock, PR_TRUE);
00431     return;
00432 }