home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / nsprpub / pr / src / threads / combined / prulock.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  12.3 KB  |  421 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. #include "primpl.h"
  20.  
  21.  
  22. void _PR_InitLocks(void)
  23. {
  24.     _PR_MD_INIT_LOCKS();
  25. }
  26.  
  27. /*
  28. ** Deal with delayed interrupts/requested reschedule during interrupt
  29. ** re-enables.
  30. */
  31. void _PR_IntsOn(_PRCPU *cpu)
  32. {
  33.     PRUintn missed, pri, i;
  34.     _PRInterruptTable *it;
  35.     PRThread *me;
  36.  
  37.     PR_ASSERT(cpu);   /* Global threads don't have CPUs */
  38.     PR_ASSERT(_PR_MD_GET_INTSOFF() > 0);
  39.     me = _PR_MD_CURRENT_THREAD();
  40. #if !defined(XP_MAC)
  41.     PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
  42. #endif
  43.  
  44.     /*
  45.     ** Process delayed interrupts. This logic is kinda scary because we
  46.     ** need to avoid losing an interrupt (it's ok to delay an interrupt
  47.     ** until later).
  48.     **
  49.     ** There are two missed state words. _pr_ints.where indicates to the
  50.     ** interrupt handler which state word is currently safe for
  51.     ** modification.
  52.     **
  53.     ** This code scans both interrupt state words, using the where flag
  54.     ** to indicate to the interrupt which state word is safe for writing.
  55.     ** If an interrupt comes in during a scan the other word will be
  56.     ** modified. This modification will be noticed during the next
  57.     ** iteration of the loop or during the next call to this routine.
  58.     */
  59.     for (i = 0; i < 2; i++) {
  60.         cpu->where = (1 - i);
  61.         missed = cpu->u.missed[i];
  62.         if (missed != 0) {
  63.             cpu->u.missed[i] = 0;
  64.             for (it = _pr_interruptTable; it->name; it++) {
  65.                 if (missed & it->missed_bit) {
  66. #ifndef XP_MAC
  67.                     PR_LOG(_pr_sched_lm, PR_LOG_MIN,
  68.                            ("IntsOn[0]: %s intr", it->name));
  69. #endif
  70.                     (*it->handler)();
  71.                 }
  72.             }
  73.         }
  74.     }
  75.  
  76.     if (cpu->u.missed[3] != 0) {
  77.         _PRCPU *cpu;
  78.  
  79.         _PR_THREAD_LOCK(me);
  80.         me->state = _PR_RUNNABLE;
  81.         pri = me->priority;
  82.  
  83.         cpu = me->cpu;
  84.         _PR_RUNQ_LOCK(cpu);
  85.         _PR_ADD_RUNQ(me, cpu, pri);
  86.         _PR_RUNQ_UNLOCK(cpu);
  87.         _PR_THREAD_UNLOCK(me);
  88.         _PR_MD_SWITCH_CONTEXT(me);
  89.     }
  90. }
  91.  
  92. /*
  93. ** Assign an idle lock to the first runnable waiting thread. Skip over
  94. ** threads that are trying to be suspended
  95. ** Note: Caller must hold _PR_LOCK_LOCK()
  96. */
  97. PRThread * _PR_AssignLock(PRLock *lock)
  98. {
  99.     PRThread *t = NULL;
  100.     PRThread *me;
  101.     PRCList *q;
  102.     PRThreadPriority pri;
  103.  
  104.     q = lock->waitQ.next;
  105.     PR_ASSERT(q != &lock->waitQ);
  106.     while (q != &lock->waitQ) {
  107.         /* Assign lock to first waiter */
  108.         t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next);
  109.  
  110.         /* 
  111.         ** We are about to change the thread's state to runnable and for local
  112.         ** threads, we are going to assign a cpu to it.  So, protect thread's
  113.         ** data structure.
  114.         */
  115.         _PR_THREAD_LOCK(t);
  116.  
  117.         if (t->flags & _PR_SUSPENDING) {
  118.             q = q->next;
  119.             _PR_THREAD_UNLOCK(t);
  120.             t = NULL;
  121.             continue;
  122.         }
  123.  
  124.         /* Found a thread to give the lock to */
  125.         PR_ASSERT(t->state == _PR_LOCK_WAIT);
  126.         PR_ASSERT(t->wait.lock == lock);
  127.         pri = t->priority;
  128.         t->wait.lock = 0;
  129.         PR_REMOVE_LINK(&t->waitQLinks);         /* take it off lock's waitQ */
  130.  
  131.         /* Lock inherits the thread's priority */
  132.         lock->priority = pri;
  133.         lock->boostPriority = PR_PRIORITY_LOW;
  134.         lock->owner = t;
  135.  
  136.         /* Add the granted lock to this owning thread's lock list */
  137.         PR_APPEND_LINK(&lock->links, &t->lockList);
  138.  
  139.         /*
  140.         ** If the new owner of the lock is a native thread, nothing else to do
  141.         ** except to wake it up by calling the machine dependent wakeup routine.
  142.         **
  143.         ** If the new owner is a local thread, we need to assign it a cpu and
  144.         ** put the thread on that cpu's run queue.  There are two cases to take care 
  145.         ** of.  If the currently running thread is also a local thread, we just
  146.         ** assign our own cpu to that thread and put it on the cpu's run queue.
  147.         ** If the the currently running thread is a native thread, we assign the
  148.         ** primordial cpu to it (on NT, MD_WAKEUP handles the cpu assignment).  
  149.         */
  150.         
  151.         if ( !_PR_IS_NATIVE_THREAD(t) ) {
  152.  
  153.             t->state = _PR_RUNNABLE;
  154.  
  155.             me = _PR_MD_CURRENT_THREAD();
  156.  
  157.             _PR_AddThreadToRunQ(me, t);
  158.             _PR_THREAD_UNLOCK(t);
  159.         } else {
  160.             t->state = _PR_RUNNING;
  161.             _PR_THREAD_UNLOCK(t);
  162.         }
  163.         _PR_MD_WAKEUP_WAITER(t);
  164.         break;
  165.     }
  166.     return t;
  167. }
  168.  
  169. /************************************************************************/
  170.  
  171.  
  172. PR_IMPLEMENT(PRLock*) PR_NewLock(void)
  173. {
  174.     PRLock *lock;
  175.  
  176.     if (!_pr_initialized) _PR_ImplicitInitialization();
  177.  
  178.     lock = PR_NEWZAP(PRLock);
  179.     if (lock) {
  180.         if (_PR_MD_NEW_LOCK(&lock->ilock) == PR_FAILURE) {
  181.         PR_DELETE(lock);
  182.         return(NULL);
  183.     }
  184.         PR_INIT_CLIST(&lock->links);
  185.         PR_INIT_CLIST(&lock->waitQ);
  186.     }
  187.     return lock;
  188. }
  189.  
  190. /*
  191. ** Destroy the given lock "lock". There is no point in making this race
  192. ** free because if some other thread has the pointer to this lock all
  193. ** bets are off.
  194. */
  195. PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock)
  196. {
  197.     PR_ASSERT(lock->owner == 0);
  198.     _PR_MD_FREE_LOCK(&lock->ilock);
  199.     PR_DELETE(lock);
  200. }
  201.  
  202. extern PRThread *suspendAllThread;
  203. /*
  204. ** Lock the lock.
  205. */
  206. PR_IMPLEMENT(void) PR_Lock(PRLock *lock)
  207. {
  208.     PRThread *me = _PR_MD_CURRENT_THREAD();
  209.     PRIntn is;
  210.     PRThread *t;
  211.     PRCList *q;
  212.  
  213.     PR_ASSERT(me != suspendAllThread); 
  214. #if !defined(XP_MAC)
  215.     PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
  216. #endif
  217. #ifdef _PR_GLOBAL_THREADS_ONLY 
  218.     PR_ASSERT(lock->owner != me);
  219.     _PR_MD_LOCK(&lock->ilock);
  220.     lock->owner = me;
  221.     return;
  222. #else  /* _PR_GLOBAL_THREADS_ONLY */
  223.  
  224.     if (!_PR_IS_NATIVE_THREAD(me))
  225.         _PR_INTSOFF(is);
  226.  
  227.     PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
  228.  
  229.     _PR_LOCK_LOCK(lock);
  230.     if (lock->owner == 0) {
  231.         /* Just got the lock */
  232.         lock->owner = me;
  233.         lock->priority = me->priority;
  234.         /* Add the granted lock to this owning thread's lock list */
  235.         PR_APPEND_LINK(&lock->links, &me->lockList);
  236.         _PR_LOCK_UNLOCK(lock);
  237.         if (!_PR_IS_NATIVE_THREAD(me))
  238.             _PR_FAST_INTSON(is);
  239.         return;
  240.     }
  241.  
  242.     /* If this thread already owns this lock, then it is a deadlock */
  243.     PR_ASSERT(lock->owner != me);
  244.  
  245.     PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
  246.  
  247. #if 0
  248.     if (me->priority > lock->owner->priority) {
  249.         /*
  250.         ** Give the lock owner a priority boost until we get the
  251.         ** lock. Record the priority we boosted it to.
  252.         */
  253.         lock->boostPriority = me->priority;
  254.         _PR_SetThreadPriority(lock->owner, me->priority);
  255.     }
  256. #endif
  257.  
  258.     /* 
  259.     Add this thread to the asked for lock's list of waiting threads.  We
  260.     add this thread thread in the right priority order so when the unlock
  261.     occurs, the thread with the higher priority will get the lock.
  262.     */
  263.     /* Sort thread into lock's waitQ at appropriate point */
  264.     q = lock->waitQ.next;
  265.  
  266.     /* Now scan the list for where to insert this entry */
  267.     while (q != &lock->waitQ) {
  268.         t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next);
  269.         if (me->priority > t->priority) {
  270.             /* Found a lower priority thread to insert in front of */
  271.             break;
  272.         }
  273.         q = q->next;
  274.     }
  275.     PR_INSERT_BEFORE(&me->waitQLinks, q);
  276.  
  277.     /* 
  278.     Now grab the threadLock since we are about to change the state.  We have
  279.     to do this since a PR_Suspend or PR_SetThreadPriority type call that takes
  280.     a PRThread* as an argument could be changing the state of this thread from
  281.     a thread running on a different cpu.
  282.     */
  283.  
  284.     _PR_THREAD_LOCK(me);
  285.     me->state = _PR_LOCK_WAIT;
  286.     me->wait.lock = lock;
  287.     _PR_THREAD_UNLOCK(me);
  288.  
  289.     _PR_LOCK_UNLOCK(lock);
  290.  
  291.     _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
  292.  
  293.     /* When we are here after the context switch, we better own the lock */
  294.     PR_ASSERT(lock->owner == me);
  295.  
  296.     if (!_PR_IS_NATIVE_THREAD(me))
  297.         _PR_FAST_INTSON(is);
  298. #endif  /* _PR_GLOBAL_THREADS_ONLY */
  299. }
  300.  
  301. /*
  302. ** Unlock the lock.
  303. */
  304. PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock)
  305. {
  306.     PRCList *q;
  307.     PRThreadPriority pri, boost;
  308.     PRIntn is;
  309.     PRThread *me = _PR_MD_CURRENT_THREAD();
  310.  
  311.     PR_ASSERT(lock->owner == me);
  312.     PR_ASSERT(me != suspendAllThread); 
  313. #if !defined(XP_MAC)
  314.     PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
  315. #endif
  316.     if (lock->owner != me) {
  317.         return PR_FAILURE;
  318.     }
  319.  
  320. #ifdef _PR_GLOBAL_THREADS_ONLY 
  321.     lock->owner = 0;
  322.     _PR_MD_UNLOCK(&lock->ilock);
  323.     return PR_SUCCESS;
  324. #else  /* _PR_GLOBAL_THREADS_ONLY */
  325.  
  326.     if (!_PR_IS_NATIVE_THREAD(me))
  327.         _PR_INTSOFF(is);
  328.     _PR_LOCK_LOCK(lock);
  329.  
  330.     /* Remove the lock from the owning thread's lock list */
  331.     PR_REMOVE_LINK(&lock->links);
  332.     pri = lock->priority;
  333.     boost = lock->boostPriority;
  334.     if (boost > pri) {
  335.         /*
  336.         ** We received a priority boost during the time we held the lock.
  337.         ** We need to figure out what priority to move to by scanning
  338.         ** down our list of lock's that we are still holding and using
  339.         ** the highest boosted priority found.
  340.         */
  341.         q = me->lockList.next;
  342.         while (q != &me->lockList) {
  343.             PRLock *ll = _PR_LOCK_PTR(q);
  344.             if (ll->boostPriority > pri) {
  345.                 pri = ll->boostPriority;
  346.             }
  347.             q = q->next;
  348.         }
  349.         if (pri != me->priority) {
  350.             _PR_SetThreadPriority(me, pri);
  351.         }
  352.     }
  353.  
  354.     /* Assign the lock to the first waiting thread */
  355.     q = lock->waitQ.next;
  356.     if (q == &lock->waitQ) {
  357.         /* Nobody wants the lock right now */
  358.         lock->boostPriority = PR_PRIORITY_LOW;
  359.         lock->owner = 0;
  360.     } else {
  361.       if (_PR_AssignLock(lock) == NULL) {
  362.     /* no eligible thread to assign to */
  363.         lock->boostPriority = PR_PRIORITY_LOW;
  364.         lock->owner = 0;
  365.       }
  366.     }
  367.     _PR_LOCK_UNLOCK(lock);
  368.     if (!_PR_IS_NATIVE_THREAD(me))
  369.         _PR_INTSON(is);
  370.     return PR_SUCCESS;
  371. #endif  /* _PR_GLOBAL_THREADS_ONLY */
  372. }
  373.  
  374. /*
  375. ** Test and then lock the lock if it's not already locked by some other
  376. ** thread. Return PR_FALSE if some other thread owned the lock at the
  377. ** time of the call.
  378. */
  379. PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock)
  380. {
  381.     PRThread *me = _PR_MD_CURRENT_THREAD();
  382.     PRBool rv = PR_FALSE;
  383.     PRIntn is;
  384.  
  385. #ifdef _PR_GLOBAL_THREADS_ONLY 
  386.     is = _PR_MD_TEST_AND_LOCK(&lock->ilock);
  387.     if (is == 0) {
  388.         lock->owner = me;
  389.         return PR_TRUE;
  390.     }
  391.     return PR_FALSE;
  392. #else  /* _PR_GLOBAL_THREADS_ONLY */
  393.  
  394.     if (!_PR_IS_NATIVE_THREAD(me))
  395.         _PR_INTSOFF(is);
  396.  
  397.     _PR_LOCK_LOCK(lock);
  398.     if (lock->owner == 0) {
  399.         /* Just got the lock */
  400.         lock->owner = me;
  401.         lock->priority = me->priority;
  402.         /* Add the granted lock to this owning thread's lock list */
  403.         PR_APPEND_LINK(&lock->links, &me->lockList);
  404.         rv = PR_TRUE;
  405.     }
  406.     _PR_LOCK_UNLOCK(lock);
  407.  
  408.     if (!_PR_IS_NATIVE_THREAD(me))
  409.         _PR_INTSON(is);
  410.     return rv;
  411. #endif  /* _PR_GLOBAL_THREADS_ONLY */
  412. }
  413.  
  414. /************************************************************************/
  415. /************************************************************************/
  416. /***********************ROUTINES FOR DCE EMULATION***********************/
  417. /************************************************************************/
  418. /************************************************************************/
  419. PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock)
  420.     { return (PR_TestAndLock(lock)) ? PR_SUCCESS : PR_FAILURE; }
  421.