andre@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #include "primpl.h" andre@0: andre@0: #if defined(WIN95) andre@0: /* andre@0: ** Some local variables report warnings on Win95 because the code paths andre@0: ** using them are conditioned on HAVE_CUSTOME_USER_THREADS. andre@0: ** The pragma suppresses the warning. andre@0: ** andre@0: */ andre@0: #pragma warning(disable : 4101) andre@0: #endif andre@0: andre@0: andre@0: void _PR_InitLocks(void) andre@0: { andre@0: _PR_MD_INIT_LOCKS(); andre@0: } andre@0: andre@0: /* andre@0: ** Deal with delayed interrupts/requested reschedule during interrupt andre@0: ** re-enables. andre@0: */ andre@0: void _PR_IntsOn(_PRCPU *cpu) andre@0: { andre@0: PRUintn missed, pri, i; andre@0: _PRInterruptTable *it; andre@0: PRThread *me; andre@0: andre@0: PR_ASSERT(cpu); /* Global threads don't have CPUs */ andre@0: PR_ASSERT(_PR_MD_GET_INTSOFF() > 0); andre@0: me = _PR_MD_CURRENT_THREAD(); andre@0: PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); andre@0: andre@0: /* andre@0: ** Process delayed interrupts. This logic is kinda scary because we andre@0: ** need to avoid losing an interrupt (it's ok to delay an interrupt andre@0: ** until later). andre@0: ** andre@0: ** There are two missed state words. _pr_ints.where indicates to the andre@0: ** interrupt handler which state word is currently safe for andre@0: ** modification. andre@0: ** andre@0: ** This code scans both interrupt state words, using the where flag andre@0: ** to indicate to the interrupt which state word is safe for writing. andre@0: ** If an interrupt comes in during a scan the other word will be andre@0: ** modified. This modification will be noticed during the next andre@0: ** iteration of the loop or during the next call to this routine. andre@0: */ andre@0: for (i = 0; i < 2; i++) { andre@0: cpu->where = (1 - i); andre@0: missed = cpu->u.missed[i]; andre@0: if (missed != 0) { andre@0: cpu->u.missed[i] = 0; andre@0: for (it = _pr_interruptTable; it->name; it++) { andre@0: if (missed & it->missed_bit) { andre@0: PR_LOG(_pr_sched_lm, PR_LOG_MIN, andre@0: ("IntsOn[0]: %s intr", it->name)); andre@0: (*it->handler)(); andre@0: } andre@0: } andre@0: } andre@0: } andre@0: andre@0: if (cpu->u.missed[3] != 0) { andre@0: _PRCPU *cpu; andre@0: andre@0: _PR_THREAD_LOCK(me); andre@0: me->state = _PR_RUNNABLE; andre@0: pri = me->priority; andre@0: andre@0: cpu = me->cpu; andre@0: _PR_RUNQ_LOCK(cpu); andre@0: _PR_ADD_RUNQ(me, cpu, pri); andre@0: _PR_RUNQ_UNLOCK(cpu); andre@0: _PR_THREAD_UNLOCK(me); andre@0: _PR_MD_SWITCH_CONTEXT(me); andre@0: } andre@0: } andre@0: andre@0: /* andre@0: ** Unblock the first runnable waiting thread. Skip over andre@0: ** threads that are trying to be suspended andre@0: ** Note: Caller must hold _PR_LOCK_LOCK() andre@0: */ andre@0: void _PR_UnblockLockWaiter(PRLock *lock) andre@0: { andre@0: PRThread *t = NULL; andre@0: PRThread *me; andre@0: PRCList *q; andre@0: andre@0: q = lock->waitQ.next; andre@0: PR_ASSERT(q != &lock->waitQ); andre@0: while (q != &lock->waitQ) { andre@0: /* Unblock first waiter */ andre@0: t = _PR_THREAD_CONDQ_PTR(q); andre@0: andre@0: /* andre@0: ** We are about to change the thread's state to runnable and for local andre@0: ** threads, we are going to assign a cpu to it. So, protect thread's andre@0: ** data structure. andre@0: */ andre@0: _PR_THREAD_LOCK(t); andre@0: andre@0: if (t->flags & _PR_SUSPENDING) { andre@0: q = q->next; andre@0: _PR_THREAD_UNLOCK(t); andre@0: continue; andre@0: } andre@0: andre@0: /* Found a runnable thread */ andre@0: PR_ASSERT(t->state == _PR_LOCK_WAIT); andre@0: PR_ASSERT(t->wait.lock == lock); andre@0: t->wait.lock = 0; andre@0: PR_REMOVE_LINK(&t->waitQLinks); /* take it off lock's waitQ */ andre@0: andre@0: /* andre@0: ** If this is a native thread, nothing else to do except to wake it andre@0: ** up by calling the machine dependent wakeup routine. andre@0: ** andre@0: ** If this is a local thread, we need to assign it a cpu and andre@0: ** put the thread on that cpu's run queue. There are two cases to andre@0: ** take care of. If the currently running thread is also a local andre@0: ** thread, we just assign our own cpu to that thread and put it on andre@0: ** the cpu's run queue. If the the currently running thread is a andre@0: ** native thread, we assign the primordial cpu to it (on NT, andre@0: ** MD_WAKEUP handles the cpu assignment). andre@0: */ andre@0: andre@0: if ( !_PR_IS_NATIVE_THREAD(t) ) { andre@0: andre@0: t->state = _PR_RUNNABLE; andre@0: andre@0: me = _PR_MD_CURRENT_THREAD(); andre@0: andre@0: _PR_AddThreadToRunQ(me, t); andre@0: _PR_THREAD_UNLOCK(t); andre@0: } else { andre@0: t->state = _PR_RUNNING; andre@0: _PR_THREAD_UNLOCK(t); andre@0: } andre@0: _PR_MD_WAKEUP_WAITER(t); andre@0: break; andre@0: } andre@0: return; andre@0: } andre@0: andre@0: /************************************************************************/ andre@0: andre@0: andre@0: PR_IMPLEMENT(PRLock*) PR_NewLock(void) andre@0: { andre@0: PRLock *lock; andre@0: andre@0: if (!_pr_initialized) _PR_ImplicitInitialization(); andre@0: andre@0: lock = PR_NEWZAP(PRLock); andre@0: if (lock) { andre@0: if (_PR_InitLock(lock) != PR_SUCCESS) { andre@0: PR_DELETE(lock); andre@0: return NULL; andre@0: } andre@0: } andre@0: return lock; andre@0: } andre@0: andre@0: PRStatus _PR_InitLock(PRLock *lock) andre@0: { andre@0: if (_PR_MD_NEW_LOCK(&lock->ilock) != PR_SUCCESS) { andre@0: return PR_FAILURE; andre@0: } andre@0: PR_INIT_CLIST(&lock->links); andre@0: PR_INIT_CLIST(&lock->waitQ); andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: /* andre@0: ** Destroy the given lock "lock". There is no point in making this race andre@0: ** free because if some other thread has the pointer to this lock all andre@0: ** bets are off. andre@0: */ andre@0: PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock) andre@0: { andre@0: _PR_FreeLock(lock); andre@0: PR_DELETE(lock); andre@0: } andre@0: andre@0: void _PR_FreeLock(PRLock *lock) andre@0: { andre@0: PR_ASSERT(lock->owner == 0); andre@0: _PR_MD_FREE_LOCK(&lock->ilock); andre@0: } andre@0: andre@0: extern PRThread *suspendAllThread; andre@0: /* andre@0: ** Lock the lock. andre@0: */ andre@0: PR_IMPLEMENT(void) PR_Lock(PRLock *lock) andre@0: { andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: PRIntn is; andre@0: PRThread *t; andre@0: PRCList *q; andre@0: andre@0: PR_ASSERT(me != suspendAllThread); andre@0: PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); andre@0: PR_ASSERT(lock != NULL); andre@0: #ifdef _PR_GLOBAL_THREADS_ONLY andre@0: _PR_MD_LOCK(&lock->ilock); andre@0: PR_ASSERT(lock->owner == 0); andre@0: lock->owner = me; andre@0: return; andre@0: #else /* _PR_GLOBAL_THREADS_ONLY */ andre@0: andre@0: if (_native_threads_only) { andre@0: _PR_MD_LOCK(&lock->ilock); andre@0: PR_ASSERT(lock->owner == 0); andre@0: lock->owner = me; andre@0: return; andre@0: } andre@0: andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSOFF(is); andre@0: andre@0: PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); andre@0: andre@0: retry: andre@0: _PR_LOCK_LOCK(lock); andre@0: if (lock->owner == 0) { andre@0: /* Just got the lock */ andre@0: lock->owner = me; andre@0: lock->priority = me->priority; andre@0: /* Add the granted lock to this owning thread's lock list */ andre@0: PR_APPEND_LINK(&lock->links, &me->lockList); andre@0: _PR_LOCK_UNLOCK(lock); andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_FAST_INTSON(is); andre@0: return; andre@0: } andre@0: andre@0: /* If this thread already owns this lock, then it is a deadlock */ andre@0: PR_ASSERT(lock->owner != me); andre@0: andre@0: PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); andre@0: andre@0: #if 0 andre@0: if (me->priority > lock->owner->priority) { andre@0: /* andre@0: ** Give the lock owner a priority boost until we get the andre@0: ** lock. Record the priority we boosted it to. andre@0: */ andre@0: lock->boostPriority = me->priority; andre@0: _PR_SetThreadPriority(lock->owner, me->priority); andre@0: } andre@0: #endif andre@0: andre@0: /* andre@0: Add this thread to the asked for lock's list of waiting threads. We andre@0: add this thread thread in the right priority order so when the unlock andre@0: occurs, the thread with the higher priority will get the lock. andre@0: */ andre@0: q = lock->waitQ.next; andre@0: if (q == &lock->waitQ || _PR_THREAD_CONDQ_PTR(q)->priority == andre@0: _PR_THREAD_CONDQ_PTR(lock->waitQ.prev)->priority) { andre@0: /* andre@0: * If all the threads in the lock waitQ have the same priority, andre@0: * then avoid scanning the list: insert the element at the end. andre@0: */ andre@0: q = &lock->waitQ; andre@0: } else { andre@0: /* Sort thread into lock's waitQ at appropriate point */ andre@0: /* Now scan the list for where to insert this entry */ andre@0: while (q != &lock->waitQ) { andre@0: t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next); andre@0: if (me->priority > t->priority) { andre@0: /* Found a lower priority thread to insert in front of */ andre@0: break; andre@0: } andre@0: q = q->next; andre@0: } andre@0: } andre@0: PR_INSERT_BEFORE(&me->waitQLinks, q); andre@0: andre@0: /* andre@0: Now grab the threadLock since we are about to change the state. We have andre@0: to do this since a PR_Suspend or PR_SetThreadPriority type call that takes andre@0: a PRThread* as an argument could be changing the state of this thread from andre@0: a thread running on a different cpu. andre@0: */ andre@0: andre@0: _PR_THREAD_LOCK(me); andre@0: me->state = _PR_LOCK_WAIT; andre@0: me->wait.lock = lock; andre@0: _PR_THREAD_UNLOCK(me); andre@0: andre@0: _PR_LOCK_UNLOCK(lock); andre@0: andre@0: _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT); andre@0: goto retry; andre@0: andre@0: #endif /* _PR_GLOBAL_THREADS_ONLY */ andre@0: } andre@0: andre@0: /* andre@0: ** Unlock the lock. andre@0: */ andre@0: PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock) andre@0: { andre@0: PRCList *q; andre@0: PRThreadPriority pri, boost; andre@0: PRIntn is; andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: andre@0: PR_ASSERT(lock != NULL); andre@0: PR_ASSERT(lock->owner == me); andre@0: PR_ASSERT(me != suspendAllThread); andre@0: PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); andre@0: if (lock->owner != me) { andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: #ifdef _PR_GLOBAL_THREADS_ONLY andre@0: lock->owner = 0; andre@0: _PR_MD_UNLOCK(&lock->ilock); andre@0: return PR_SUCCESS; andre@0: #else /* _PR_GLOBAL_THREADS_ONLY */ andre@0: andre@0: if (_native_threads_only) { andre@0: lock->owner = 0; andre@0: _PR_MD_UNLOCK(&lock->ilock); andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSOFF(is); andre@0: _PR_LOCK_LOCK(lock); andre@0: andre@0: /* Remove the lock from the owning thread's lock list */ andre@0: PR_REMOVE_LINK(&lock->links); andre@0: pri = lock->priority; andre@0: boost = lock->boostPriority; andre@0: if (boost > pri) { andre@0: /* andre@0: ** We received a priority boost during the time we held the lock. andre@0: ** We need to figure out what priority to move to by scanning andre@0: ** down our list of lock's that we are still holding and using andre@0: ** the highest boosted priority found. andre@0: */ andre@0: q = me->lockList.next; andre@0: while (q != &me->lockList) { andre@0: PRLock *ll = _PR_LOCK_PTR(q); andre@0: if (ll->boostPriority > pri) { andre@0: pri = ll->boostPriority; andre@0: } andre@0: q = q->next; andre@0: } andre@0: if (pri != me->priority) { andre@0: _PR_SetThreadPriority(me, pri); andre@0: } andre@0: } andre@0: andre@0: /* Unblock the first waiting thread */ andre@0: q = lock->waitQ.next; andre@0: if (q != &lock->waitQ) andre@0: _PR_UnblockLockWaiter(lock); andre@0: lock->boostPriority = PR_PRIORITY_LOW; andre@0: lock->owner = 0; andre@0: _PR_LOCK_UNLOCK(lock); andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSON(is); andre@0: return PR_SUCCESS; andre@0: #endif /* _PR_GLOBAL_THREADS_ONLY */ andre@0: } andre@0: andre@0: /* andre@0: ** If the current thread owns |lock|, this assertion is guaranteed to andre@0: ** succeed. Otherwise, the behavior of this function is undefined. andre@0: */ andre@0: PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock *lock) andre@0: { andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: PR_ASSERT(lock->owner == me); andre@0: } andre@0: andre@0: /* andre@0: ** Test and then lock the lock if it's not already locked by some other andre@0: ** thread. Return PR_FALSE if some other thread owned the lock at the andre@0: ** time of the call. andre@0: */ andre@0: PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock) andre@0: { andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: PRBool rv = PR_FALSE; andre@0: PRIntn is; andre@0: andre@0: #ifdef _PR_GLOBAL_THREADS_ONLY andre@0: is = _PR_MD_TEST_AND_LOCK(&lock->ilock); andre@0: if (is == 0) { andre@0: lock->owner = me; andre@0: return PR_TRUE; andre@0: } andre@0: return PR_FALSE; andre@0: #else /* _PR_GLOBAL_THREADS_ONLY */ andre@0: andre@0: #ifndef _PR_LOCAL_THREADS_ONLY andre@0: if (_native_threads_only) { andre@0: is = _PR_MD_TEST_AND_LOCK(&lock->ilock); andre@0: if (is == 0) { andre@0: lock->owner = me; andre@0: return PR_TRUE; andre@0: } andre@0: return PR_FALSE; andre@0: } andre@0: #endif andre@0: andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSOFF(is); andre@0: andre@0: _PR_LOCK_LOCK(lock); andre@0: if (lock->owner == 0) { andre@0: /* Just got the lock */ andre@0: lock->owner = me; andre@0: lock->priority = me->priority; andre@0: /* Add the granted lock to this owning thread's lock list */ andre@0: PR_APPEND_LINK(&lock->links, &me->lockList); andre@0: rv = PR_TRUE; andre@0: } andre@0: _PR_LOCK_UNLOCK(lock); andre@0: andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSON(is); andre@0: return rv; andre@0: #endif /* _PR_GLOBAL_THREADS_ONLY */ andre@0: } andre@0: andre@0: /************************************************************************/ andre@0: /************************************************************************/ andre@0: /***********************ROUTINES FOR DCE EMULATION***********************/ andre@0: /************************************************************************/ andre@0: /************************************************************************/ andre@0: PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock) andre@0: { return (PR_TestAndLock(lock)) ? PR_SUCCESS : PR_FAILURE; }