Back to index

lightning-sunbird  0.9+nobinonly
prulock.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 #include "primpl.h"
00039 
00040 #if defined(WIN95)
00041 /*
00042 ** Some local variables report warnings on Win95 because the code paths 
00043 ** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
00044 ** The pragma suppresses the warning.
00045 ** 
00046 */
00047 #pragma warning(disable : 4101)
00048 #endif
00049 
00050 
00051 void _PR_InitLocks(void)
00052 {
00053        _PR_MD_INIT_LOCKS();
00054 }
00055 
00056 /*
00057 ** Deal with delayed interrupts/requested reschedule during interrupt
00058 ** re-enables.
00059 */
00060 void _PR_IntsOn(_PRCPU *cpu)
00061 {
00062     PRUintn missed, pri, i;
00063     _PRInterruptTable *it;
00064     PRThread *me;
00065 
00066     PR_ASSERT(cpu);   /* Global threads don't have CPUs */
00067     PR_ASSERT(_PR_MD_GET_INTSOFF() > 0);
00068        me = _PR_MD_CURRENT_THREAD();
00069 #if !defined(XP_MAC)
00070     PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
00071 #endif
00072 
00073     /*
00074     ** Process delayed interrupts. This logic is kinda scary because we
00075     ** need to avoid losing an interrupt (it's ok to delay an interrupt
00076     ** until later).
00077     **
00078     ** There are two missed state words. _pr_ints.where indicates to the
00079     ** interrupt handler which state word is currently safe for
00080     ** modification.
00081     **
00082     ** This code scans both interrupt state words, using the where flag
00083     ** to indicate to the interrupt which state word is safe for writing.
00084     ** If an interrupt comes in during a scan the other word will be
00085     ** modified. This modification will be noticed during the next
00086     ** iteration of the loop or during the next call to this routine.
00087     */
00088     for (i = 0; i < 2; i++) {
00089         cpu->where = (1 - i);
00090         missed = cpu->u.missed[i];
00091         if (missed != 0) {
00092             cpu->u.missed[i] = 0;
00093             for (it = _pr_interruptTable; it->name; it++) {
00094                 if (missed & it->missed_bit) {
00095 #ifndef XP_MAC
00096                     PR_LOG(_pr_sched_lm, PR_LOG_MIN,
00097                            ("IntsOn[0]: %s intr", it->name));
00098 #endif
00099                     (*it->handler)();
00100                 }
00101             }
00102         }
00103     }
00104 
00105     if (cpu->u.missed[3] != 0) {
00106         _PRCPU *cpu;
00107 
00108               _PR_THREAD_LOCK(me);
00109         me->state = _PR_RUNNABLE;
00110         pri = me->priority;
00111 
00112         cpu = me->cpu;
00113               _PR_RUNQ_LOCK(cpu);
00114         _PR_ADD_RUNQ(me, cpu, pri);
00115               _PR_RUNQ_UNLOCK(cpu);
00116               _PR_THREAD_UNLOCK(me);
00117         _PR_MD_SWITCH_CONTEXT(me);
00118     }
00119 }
00120 
00121 /*
00122 ** Unblock the first runnable waiting thread. Skip over
00123 ** threads that are trying to be suspended
00124 ** Note: Caller must hold _PR_LOCK_LOCK()
00125 */
00126 void _PR_UnblockLockWaiter(PRLock *lock)
00127 {
00128     PRThread *t = NULL;
00129     PRThread *me;
00130     PRCList *q;
00131 
00132     q = lock->waitQ.next;
00133     PR_ASSERT(q != &lock->waitQ);
00134     while (q != &lock->waitQ) {
00135         /* Unblock first waiter */
00136         t = _PR_THREAD_CONDQ_PTR(q);
00137 
00138               /* 
00139               ** We are about to change the thread's state to runnable and for local
00140               ** threads, we are going to assign a cpu to it.  So, protect thread's
00141               ** data structure.
00142               */
00143         _PR_THREAD_LOCK(t);
00144 
00145         if (t->flags & _PR_SUSPENDING) {
00146             q = q->next;
00147             _PR_THREAD_UNLOCK(t);
00148             continue;
00149         }
00150 
00151         /* Found a runnable thread */
00152            PR_ASSERT(t->state == _PR_LOCK_WAIT);
00153            PR_ASSERT(t->wait.lock == lock);
00154         t->wait.lock = 0;
00155         PR_REMOVE_LINK(&t->waitQLinks);         /* take it off lock's waitQ */
00156 
00157               /*
00158               ** If this is a native thread, nothing else to do except to wake it
00159               ** up by calling the machine dependent wakeup routine.
00160               **
00161               ** If this is a local thread, we need to assign it a cpu and
00162               ** put the thread on that cpu's run queue.  There are two cases to
00163               ** take care of.  If the currently running thread is also a local
00164               ** thread, we just assign our own cpu to that thread and put it on
00165               ** the cpu's run queue.  If the the currently running thread is a
00166               ** native thread, we assign the primordial cpu to it (on NT,
00167               ** MD_WAKEUP handles the cpu assignment).  
00168               */
00169               
00170         if ( !_PR_IS_NATIVE_THREAD(t) ) {
00171 
00172             t->state = _PR_RUNNABLE;
00173 
00174             me = _PR_MD_CURRENT_THREAD();
00175 
00176             _PR_AddThreadToRunQ(me, t);
00177             _PR_THREAD_UNLOCK(t);
00178         } else {
00179             t->state = _PR_RUNNING;
00180             _PR_THREAD_UNLOCK(t);
00181         }
00182         _PR_MD_WAKEUP_WAITER(t);
00183         break;
00184     }
00185     return;
00186 }
00187 
00188 /************************************************************************/
00189 
00190 
00191 PR_IMPLEMENT(PRLock*) PR_NewLock(void)
00192 {
00193     PRLock *lock;
00194 
00195     if (!_pr_initialized) _PR_ImplicitInitialization();
00196 
00197     lock = PR_NEWZAP(PRLock);
00198     if (lock) {
00199         if (_PR_MD_NEW_LOCK(&lock->ilock) == PR_FAILURE) {
00200               PR_DELETE(lock);
00201               return(NULL);
00202        }
00203         PR_INIT_CLIST(&lock->links);
00204         PR_INIT_CLIST(&lock->waitQ);
00205     }
00206     return lock;
00207 }
00208 
00209 /*
00210 ** Destroy the given lock "lock". There is no point in making this race
00211 ** free because if some other thread has the pointer to this lock all
00212 ** bets are off.
00213 */
00214 PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock)
00215 {
00216     PR_ASSERT(lock->owner == 0);
00217        _PR_MD_FREE_LOCK(&lock->ilock);
00218     PR_DELETE(lock);
00219 }
00220 
00221 extern PRThread *suspendAllThread;
00222 /*
00223 ** Lock the lock.
00224 */
00225 PR_IMPLEMENT(void) PR_Lock(PRLock *lock)
00226 {
00227     PRThread *me = _PR_MD_CURRENT_THREAD();
00228     PRIntn is;
00229     PRThread *t;
00230     PRCList *q;
00231 
00232     PR_ASSERT(me != suspendAllThread); 
00233 #if !defined(XP_MAC)
00234     PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
00235 #endif
00236     PR_ASSERT(lock != NULL);
00237 #ifdef _PR_GLOBAL_THREADS_ONLY 
00238     PR_ASSERT(lock->owner != me);
00239     _PR_MD_LOCK(&lock->ilock);
00240     lock->owner = me;
00241     return;
00242 #else  /* _PR_GLOBAL_THREADS_ONLY */
00243 
00244        if (_native_threads_only) {
00245               PR_ASSERT(lock->owner != me);
00246               _PR_MD_LOCK(&lock->ilock);
00247               lock->owner = me;
00248               return;
00249        }
00250 
00251     if (!_PR_IS_NATIVE_THREAD(me))
00252        _PR_INTSOFF(is);
00253 
00254     PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
00255 
00256 retry:
00257     _PR_LOCK_LOCK(lock);
00258     if (lock->owner == 0) {
00259         /* Just got the lock */
00260         lock->owner = me;
00261         lock->priority = me->priority;
00262               /* Add the granted lock to this owning thread's lock list */
00263         PR_APPEND_LINK(&lock->links, &me->lockList);
00264         _PR_LOCK_UNLOCK(lock);
00265        if (!_PR_IS_NATIVE_THREAD(me))
00266               _PR_FAST_INTSON(is);
00267         return;
00268     }
00269 
00270     /* If this thread already owns this lock, then it is a deadlock */
00271     PR_ASSERT(lock->owner != me);
00272 
00273     PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
00274 
00275 #if 0
00276     if (me->priority > lock->owner->priority) {
00277         /*
00278         ** Give the lock owner a priority boost until we get the
00279         ** lock. Record the priority we boosted it to.
00280         */
00281         lock->boostPriority = me->priority;
00282         _PR_SetThreadPriority(lock->owner, me->priority);
00283     }
00284 #endif
00285 
00286     /* 
00287     Add this thread to the asked for lock's list of waiting threads.  We
00288     add this thread thread in the right priority order so when the unlock
00289     occurs, the thread with the higher priority will get the lock.
00290     */
00291     q = lock->waitQ.next;
00292     if (q == &lock->waitQ || _PR_THREAD_CONDQ_PTR(q)->priority ==
00293        _PR_THREAD_CONDQ_PTR(lock->waitQ.prev)->priority) {
00294               /*
00295                * If all the threads in the lock waitQ have the same priority,
00296                * then avoid scanning the list:  insert the element at the end.
00297                */
00298               q = &lock->waitQ;
00299     } else {
00300               /* Sort thread into lock's waitQ at appropriate point */
00301               /* Now scan the list for where to insert this entry */
00302               while (q != &lock->waitQ) {
00303                      t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next);
00304                      if (me->priority > t->priority) {
00305                             /* Found a lower priority thread to insert in front of */
00306                             break;
00307                      }
00308                      q = q->next;
00309               }
00310        }
00311     PR_INSERT_BEFORE(&me->waitQLinks, q);
00312 
00313        /* 
00314        Now grab the threadLock since we are about to change the state.  We have
00315        to do this since a PR_Suspend or PR_SetThreadPriority type call that takes
00316        a PRThread* as an argument could be changing the state of this thread from
00317        a thread running on a different cpu.
00318        */
00319 
00320     _PR_THREAD_LOCK(me);
00321     me->state = _PR_LOCK_WAIT;
00322     me->wait.lock = lock;
00323     _PR_THREAD_UNLOCK(me);
00324 
00325     _PR_LOCK_UNLOCK(lock);
00326 
00327     _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
00328        goto retry;
00329 
00330 #endif  /* _PR_GLOBAL_THREADS_ONLY */
00331 }
00332 
00333 /*
00334 ** Unlock the lock.
00335 */
00336 PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock)
00337 {
00338     PRCList *q;
00339     PRThreadPriority pri, boost;
00340     PRIntn is;
00341     PRThread *me = _PR_MD_CURRENT_THREAD();
00342 
00343     PR_ASSERT(lock != NULL);
00344     PR_ASSERT(lock->owner == me);
00345     PR_ASSERT(me != suspendAllThread); 
00346 #if !defined(XP_MAC)
00347     PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
00348 #endif
00349     if (lock->owner != me) {
00350         return PR_FAILURE;
00351     }
00352 
00353 #ifdef _PR_GLOBAL_THREADS_ONLY 
00354     lock->owner = 0;
00355     _PR_MD_UNLOCK(&lock->ilock);
00356     return PR_SUCCESS;
00357 #else  /* _PR_GLOBAL_THREADS_ONLY */
00358 
00359        if (_native_threads_only) {
00360               lock->owner = 0;
00361               _PR_MD_UNLOCK(&lock->ilock);
00362               return PR_SUCCESS;
00363        }
00364 
00365     if (!_PR_IS_NATIVE_THREAD(me))
00366        _PR_INTSOFF(is);
00367     _PR_LOCK_LOCK(lock);
00368 
00369        /* Remove the lock from the owning thread's lock list */
00370     PR_REMOVE_LINK(&lock->links);
00371     pri = lock->priority;
00372     boost = lock->boostPriority;
00373     if (boost > pri) {
00374         /*
00375         ** We received a priority boost during the time we held the lock.
00376         ** We need to figure out what priority to move to by scanning
00377         ** down our list of lock's that we are still holding and using
00378         ** the highest boosted priority found.
00379         */
00380         q = me->lockList.next;
00381         while (q != &me->lockList) {
00382             PRLock *ll = _PR_LOCK_PTR(q);
00383             if (ll->boostPriority > pri) {
00384                 pri = ll->boostPriority;
00385             }
00386             q = q->next;
00387         }
00388         if (pri != me->priority) {
00389             _PR_SetThreadPriority(me, pri);
00390         }
00391     }
00392 
00393     /* Unblock the first waiting thread */
00394     q = lock->waitQ.next;
00395     if (q != &lock->waitQ)
00396         _PR_UnblockLockWaiter(lock);
00397     lock->boostPriority = PR_PRIORITY_LOW;
00398     lock->owner = 0;
00399     _PR_LOCK_UNLOCK(lock);
00400     if (!_PR_IS_NATIVE_THREAD(me))
00401        _PR_INTSON(is);
00402     return PR_SUCCESS;
00403 #endif  /* _PR_GLOBAL_THREADS_ONLY */
00404 }
00405 
00406 /*
00407 ** Test and then lock the lock if it's not already locked by some other
00408 ** thread. Return PR_FALSE if some other thread owned the lock at the
00409 ** time of the call.
00410 */
00411 PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock)
00412 {
00413     PRThread *me = _PR_MD_CURRENT_THREAD();
00414     PRBool rv = PR_FALSE;
00415     PRIntn is;
00416 
00417 #ifdef _PR_GLOBAL_THREADS_ONLY 
00418     is = _PR_MD_TEST_AND_LOCK(&lock->ilock);
00419     if (is == 0) {
00420         lock->owner = me;
00421         return PR_TRUE;
00422     }
00423     return PR_FALSE;
00424 #else  /* _PR_GLOBAL_THREADS_ONLY */
00425 
00426 #ifndef _PR_LOCAL_THREADS_ONLY
00427        if (_native_threads_only) {
00428               is = _PR_MD_TEST_AND_LOCK(&lock->ilock);
00429               if (is == 0) {
00430                      lock->owner = me;
00431                      return PR_TRUE;
00432               }
00433        return PR_FALSE;
00434        }
00435 #endif
00436 
00437     if (!_PR_IS_NATIVE_THREAD(me))
00438        _PR_INTSOFF(is);
00439 
00440     _PR_LOCK_LOCK(lock);
00441     if (lock->owner == 0) {
00442         /* Just got the lock */
00443         lock->owner = me;
00444         lock->priority = me->priority;
00445               /* Add the granted lock to this owning thread's lock list */
00446         PR_APPEND_LINK(&lock->links, &me->lockList);
00447         rv = PR_TRUE;
00448     }
00449     _PR_LOCK_UNLOCK(lock);
00450 
00451     if (!_PR_IS_NATIVE_THREAD(me))
00452        _PR_INTSON(is);
00453     return rv;
00454 #endif  /* _PR_GLOBAL_THREADS_ONLY */
00455 }
00456 
00457 /************************************************************************/
00458 /************************************************************************/
00459 /***********************ROUTINES FOR DCE EMULATION***********************/
00460 /************************************************************************/
00461 /************************************************************************/
00462 PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock)
00463     { return (PR_TestAndLock(lock)) ? PR_SUCCESS : PR_FAILURE; }