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