/* ** Notify all of the threads waiting on the condition variable. All of ** threads are notified in turn. The highest priority thread will ** probably acquire the lock. */ PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar) { PRCList *q; PRIntn is; PRThread *me = _PR_MD_CURRENT_THREAD(); PR_ASSERT(cvar->lock->owner == me); if (cvar->lock->owner != me) return PR_FAILURE; #ifdef _PR_GLOBAL_THREADS_ONLY _PR_MD_NOTIFYALL_CV(&cvar->md, &cvar->lock->ilock); return PR_SUCCESS; #else /* _PR_GLOBAL_THREADS_ONLY */ if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); _PR_CVAR_LOCK(cvar); q = cvar->condQ.next; while (q != &cvar->condQ) { PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_NotifyAll: cvar=%p", cvar)); _PR_NotifyThread(_PR_THREAD_CONDQ_PTR(q), me); q = q->next; } _PR_CVAR_UNLOCK(cvar); if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); return PR_SUCCESS; #endif /* _PR_GLOBAL_THREADS_ONLY */ }
void _PR_NotifyCondVar(PRCondVar *cvar, PRThread *me) { #ifdef _PR_GLOBAL_THREADS_ONLY _PR_MD_NOTIFY_CV(&cvar->md, &cvar->lock->ilock); #else /* _PR_GLOBAL_THREADS_ONLY */ PRCList *q; PRIntn is; if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); _PR_CVAR_LOCK(cvar); q = cvar->condQ.next; while (q != &cvar->condQ) { #ifndef XP_MAC PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("_PR_NotifyCondVar: cvar=%p", cvar)); #endif if (_PR_THREAD_CONDQ_PTR(q)->wait.cvar) { if (_PR_NotifyThread(_PR_THREAD_CONDQ_PTR(q), me) == PR_TRUE) break; } q = q->next; } _PR_CVAR_UNLOCK(cvar); if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); #endif /* _PR_GLOBAL_THREADS_ONLY */ }
_MD_CreateThread( PRThread *thread, void (*start) (void *), PRThreadPriority priority, PRThreadScope scope, PRThreadState state, PRUint32 stackSize) { PRIntn is; int rv; PRThread *me = _PR_MD_CURRENT_THREAD(); pthread_attr_t attr; if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); if (pthread_mutex_init(&thread->md.pthread_mutex, NULL) != 0) { if (!_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is); return PR_FAILURE; } if (pthread_cond_init(&thread->md.pthread_cond, NULL) != 0) { pthread_mutex_destroy(&thread->md.pthread_mutex); if (!_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is); return PR_FAILURE; } thread->flags |= _PR_GLOBAL_SCOPE; pthread_attr_init(&attr); /* initialize attr with default attributes */ if (pthread_attr_setstacksize(&attr, (size_t) stackSize) != 0) { pthread_mutex_destroy(&thread->md.pthread_mutex); pthread_cond_destroy(&thread->md.pthread_cond); pthread_attr_destroy(&attr); if (!_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is); return PR_FAILURE; } thread->md.wait = 0; rv = pthread_create(&thread->md.pthread, &attr, start, (void *)thread); if (0 == rv) { _MD_ATOMIC_INCREMENT(&_pr_md_pthreads_created); _MD_ATOMIC_INCREMENT(&_pr_md_pthreads); if (!_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is); return PR_SUCCESS; } else { pthread_mutex_destroy(&thread->md.pthread_mutex); pthread_cond_destroy(&thread->md.pthread_cond); pthread_attr_destroy(&attr); _MD_ATOMIC_INCREMENT(&_pr_md_pthreads_failed); if (!_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is); PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, rv); return PR_FAILURE; } }
PR_IMPLEMENT(void) PR_ShowStatus(void) { PRIntn is; if ( _PR_MD_CURRENT_THREAD() && !_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) _PR_INTSOFF(is); _pr_dumpOut = _pr_stderr; _PR_DumpThreads(_pr_dumpOut); if ( _PR_MD_CURRENT_THREAD() && !_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) _PR_FAST_INTSON(is); }
PR_IMPLEMENT(void) _MD_FREE_LOCK(struct _MDLock *lockp) { PRIntn _is; PRThread *me = _PR_MD_CURRENT_THREAD(); if (me && !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(_is); pthread_mutex_destroy(&lockp->mutex); if (me && !_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(_is); }
PR_IMPLEMENT(PRStatus) _MD_NEW_LOCK(struct _MDLock *lockp) { PRStatus rv; PRIntn is; PRThread *me = _PR_MD_CURRENT_THREAD(); if (me && !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); rv = pthread_mutex_init(&lockp->mutex, NULL); if (me && !_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is); return (rv == 0) ? PR_SUCCESS : PR_FAILURE; }
/* ** Test and then lock the lock if it's not already locked by some other ** thread. Return PR_FALSE if some other thread owned the lock at the ** time of the call. */ PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock) { PRThread *me = _PR_MD_CURRENT_THREAD(); PRBool rv = PR_FALSE; PRIntn is; #ifdef _PR_GLOBAL_THREADS_ONLY is = _PR_MD_TEST_AND_LOCK(&lock->ilock); if (is == 0) { lock->owner = me; return PR_TRUE; } return PR_FALSE; #else /* _PR_GLOBAL_THREADS_ONLY */ #ifndef _PR_LOCAL_THREADS_ONLY if (_native_threads_only) { is = _PR_MD_TEST_AND_LOCK(&lock->ilock); if (is == 0) { lock->owner = me; return PR_TRUE; } return PR_FALSE; } #endif if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); _PR_LOCK_LOCK(lock); if (lock->owner == 0) { /* Just got the lock */ lock->owner = me; lock->priority = me->priority; /* Add the granted lock to this owning thread's lock list */ PR_APPEND_LINK(&lock->links, &me->lockList); rv = PR_TRUE; } _PR_LOCK_UNLOCK(lock); if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); return rv; #endif /* _PR_GLOBAL_THREADS_ONLY */ }
PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar) { PRCList *q; PRIntn is; PRThread *me = _PR_MD_CURRENT_THREAD(); PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock); if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); _PR_MD_LOCK( &(cvar->ilock) ); q = cvar->condQ.next; while (q != &cvar->condQ) { PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_NotifyAll: cvar=%p", cvar)); _PR_NotifyThread(_PR_THREAD_CONDQ_PTR(q), me); q = q->next; } _PR_MD_UNLOCK( &(cvar->ilock) ); if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); return PR_SUCCESS; } /* PRP_NakedBroadcast */
_MD_WakeupWaiter(PRThread *thread) { PRThread *me = _PR_MD_CURRENT_THREAD(); PRInt32 pid, rv; PRIntn is; PR_ASSERT(_pr_md_idle_cpus >= 0); if (thread == NULL) { if (_pr_md_idle_cpus) _MD_Wakeup_CPUs(); } else if (!_PR_IS_NATIVE_THREAD(thread)) { /* * If the thread is on my cpu's runq there is no need to * wakeup any cpus */ if (!_PR_IS_NATIVE_THREAD(me)) { if (me->cpu != thread->cpu) { if (_pr_md_idle_cpus) _MD_Wakeup_CPUs(); } } else { if (_pr_md_idle_cpus) _MD_Wakeup_CPUs(); } } else { PR_ASSERT(_PR_IS_NATIVE_THREAD(thread)); if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); pthread_mutex_lock(&thread->md.pthread_mutex); thread->md.wait++; rv = pthread_cond_signal(&thread->md.pthread_cond); PR_ASSERT(rv == 0); pthread_mutex_unlock(&thread->md.pthread_mutex); if (!_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is); } return PR_SUCCESS; }
inline void DoneWaitingOnThisThread(PRThread thread) { int is; int thread_md_asyncIOLock; int thread_io_pending; int thread_md_asyncIOCVar; _PR_INTSOFF(is); PR_Lock(thread_md_asyncIOLock); { __ESBMC_atomic_begin(); if (__COUNT__ == 1) { thread_io_pending = PR_FALSE; // check for order violation __COUNT__ = __COUNT__ + 1; } else { assert(0); } __ESBMC_atomic_end(); } // let the waiting thread know that async IO completed PR_NotifyCondVar(thread_md_asyncIOCVar); PR_Unlock(thread_md_asyncIOLock); _PR_FAST_INTSON(is); }
/* ** Unlock the lock. */ PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock) { PRCList *q; PRThreadPriority pri, boost; PRIntn is; PRThread *me = _PR_MD_CURRENT_THREAD(); PR_ASSERT(lock != NULL); PR_ASSERT(lock->owner == me); PR_ASSERT(me != suspendAllThread); PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); if (lock->owner != me) { return PR_FAILURE; } #ifdef _PR_GLOBAL_THREADS_ONLY lock->owner = 0; _PR_MD_UNLOCK(&lock->ilock); return PR_SUCCESS; #else /* _PR_GLOBAL_THREADS_ONLY */ if (_native_threads_only) { lock->owner = 0; _PR_MD_UNLOCK(&lock->ilock); return PR_SUCCESS; } if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); _PR_LOCK_LOCK(lock); /* Remove the lock from the owning thread's lock list */ PR_REMOVE_LINK(&lock->links); pri = lock->priority; boost = lock->boostPriority; if (boost > pri) { /* ** We received a priority boost during the time we held the lock. ** We need to figure out what priority to move to by scanning ** down our list of lock's that we are still holding and using ** the highest boosted priority found. */ q = me->lockList.next; while (q != &me->lockList) { PRLock *ll = _PR_LOCK_PTR(q); if (ll->boostPriority > pri) { pri = ll->boostPriority; } q = q->next; } if (pri != me->priority) { _PR_SetThreadPriority(me, pri); } } /* Unblock the first waiting thread */ q = lock->waitQ.next; if (q != &lock->waitQ) _PR_UnblockLockWaiter(lock); lock->boostPriority = PR_PRIORITY_LOW; lock->owner = 0; _PR_LOCK_UNLOCK(lock); if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); return PR_SUCCESS; #endif /* _PR_GLOBAL_THREADS_ONLY */ }
/* ** Lock the lock. */ PR_IMPLEMENT(void) PR_Lock(PRLock *lock) { PRThread *me = _PR_MD_CURRENT_THREAD(); PRIntn is; PRThread *t; PRCList *q; PR_ASSERT(me != suspendAllThread); PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); PR_ASSERT(lock != NULL); #ifdef _PR_GLOBAL_THREADS_ONLY _PR_MD_LOCK(&lock->ilock); PR_ASSERT(lock->owner == 0); lock->owner = me; return; #else /* _PR_GLOBAL_THREADS_ONLY */ if (_native_threads_only) { _PR_MD_LOCK(&lock->ilock); PR_ASSERT(lock->owner == 0); lock->owner = me; return; } if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); retry: _PR_LOCK_LOCK(lock); if (lock->owner == 0) { /* Just got the lock */ lock->owner = me; lock->priority = me->priority; /* Add the granted lock to this owning thread's lock list */ PR_APPEND_LINK(&lock->links, &me->lockList); _PR_LOCK_UNLOCK(lock); if (!_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is); return; } /* If this thread already owns this lock, then it is a deadlock */ PR_ASSERT(lock->owner != me); PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); #if 0 if (me->priority > lock->owner->priority) { /* ** Give the lock owner a priority boost until we get the ** lock. Record the priority we boosted it to. */ lock->boostPriority = me->priority; _PR_SetThreadPriority(lock->owner, me->priority); } #endif /* Add this thread to the asked for lock's list of waiting threads. We add this thread thread in the right priority order so when the unlock occurs, the thread with the higher priority will get the lock. */ q = lock->waitQ.next; if (q == &lock->waitQ || _PR_THREAD_CONDQ_PTR(q)->priority == _PR_THREAD_CONDQ_PTR(lock->waitQ.prev)->priority) { /* * If all the threads in the lock waitQ have the same priority, * then avoid scanning the list: insert the element at the end. */ q = &lock->waitQ; } else { /* Sort thread into lock's waitQ at appropriate point */ /* Now scan the list for where to insert this entry */ while (q != &lock->waitQ) { t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next); if (me->priority > t->priority) { /* Found a lower priority thread to insert in front of */ break; } q = q->next; } } PR_INSERT_BEFORE(&me->waitQLinks, q); /* Now grab the threadLock since we are about to change the state. We have to do this since a PR_Suspend or PR_SetThreadPriority type call that takes a PRThread* as an argument could be changing the state of this thread from a thread running on a different cpu. */ _PR_THREAD_LOCK(me); me->state = _PR_LOCK_WAIT; me->wait.lock = lock; _PR_THREAD_UNLOCK(me); _PR_LOCK_UNLOCK(lock); _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT); goto retry; #endif /* _PR_GLOBAL_THREADS_ONLY */ }
/* ** Make the given thread wait for the given condition variable */ PRStatus _PR_WaitCondVar( PRThread *thread, PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout) { PRIntn is; PRStatus rv = PR_SUCCESS; PR_ASSERT(thread == _PR_MD_CURRENT_THREAD()); PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); #ifdef _PR_GLOBAL_THREADS_ONLY if (_PR_PENDING_INTERRUPT(thread)) { PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); thread->flags &= ~_PR_INTERRUPT; return PR_FAILURE; } thread->wait.cvar = cvar; lock->owner = NULL; _PR_MD_WAIT_CV(&cvar->md,&lock->ilock, timeout); thread->wait.cvar = NULL; lock->owner = thread; if (_PR_PENDING_INTERRUPT(thread)) { PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); thread->flags &= ~_PR_INTERRUPT; return PR_FAILURE; } return PR_SUCCESS; #else /* _PR_GLOBAL_THREADS_ONLY */ if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_INTSOFF(is); _PR_CVAR_LOCK(cvar); _PR_THREAD_LOCK(thread); if (_PR_PENDING_INTERRUPT(thread)) { PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); thread->flags &= ~_PR_INTERRUPT; _PR_CVAR_UNLOCK(cvar); _PR_THREAD_UNLOCK(thread); if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_INTSON(is); return PR_FAILURE; } thread->state = _PR_COND_WAIT; thread->wait.cvar = cvar; /* ** Put the caller thread on the condition variable's wait Q */ PR_APPEND_LINK(&thread->waitQLinks, &cvar->condQ); /* Note- for global scope threads, we don't put them on the * global sleepQ, so each global thread must put itself * to sleep only for the time it wants to. */ if ( !_PR_IS_NATIVE_THREAD(thread) ) { _PR_SLEEPQ_LOCK(thread->cpu); _PR_ADD_SLEEPQ(thread, timeout); _PR_SLEEPQ_UNLOCK(thread->cpu); } _PR_CVAR_UNLOCK(cvar); _PR_THREAD_UNLOCK(thread); /* ** Release lock protecting the condition variable and thereby giving time ** to the next thread which can potentially notify on the condition variable */ PR_Unlock(lock); PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_Wait: cvar=%p waiting for %d", cvar, timeout)); rv = _PR_MD_WAIT(thread, timeout); _PR_CVAR_LOCK(cvar); PR_REMOVE_LINK(&thread->waitQLinks); _PR_CVAR_UNLOCK(cvar); PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_Wait: cvar=%p done waiting", cvar)); if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_INTSON(is); /* Acquire lock again that we had just relinquished */ PR_Lock(lock); if (_PR_PENDING_INTERRUPT(thread)) { PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); thread->flags &= ~_PR_INTERRUPT; return PR_FAILURE; } return rv; #endif /* _PR_GLOBAL_THREADS_ONLY */ }