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: #include andre@0: #include 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: /* _pr_activeLock protects the following global variables */ andre@0: PRLock *_pr_activeLock; andre@0: PRInt32 _pr_primordialExitCount; /* In PR_Cleanup(), the primordial thread andre@0: * waits until all other user (non-system) andre@0: * threads have terminated before it exits. andre@0: * So whenever we decrement _pr_userActive, andre@0: * it is compared with andre@0: * _pr_primordialExitCount. andre@0: * If the primordial thread is a system andre@0: * thread, then _pr_primordialExitCount andre@0: * is 0. If the primordial thread is andre@0: * itself a user thread, then andre@0: * _pr_primordialThread is 1. andre@0: */ andre@0: PRCondVar *_pr_primordialExitCVar; /* When _pr_userActive is decremented to andre@0: * _pr_primordialExitCount, this condition andre@0: * variable is notified. andre@0: */ andre@0: andre@0: PRLock *_pr_deadQLock; andre@0: PRUint32 _pr_numNativeDead; andre@0: PRUint32 _pr_numUserDead; andre@0: PRCList _pr_deadNativeQ; andre@0: PRCList _pr_deadUserQ; andre@0: andre@0: PRUint32 _pr_join_counter; andre@0: andre@0: PRUint32 _pr_local_threads; andre@0: PRUint32 _pr_global_threads; andre@0: andre@0: PRBool suspendAllOn = PR_FALSE; andre@0: PRThread *suspendAllThread = NULL; andre@0: andre@0: extern PRCList _pr_active_global_threadQ; andre@0: extern PRCList _pr_active_local_threadQ; andre@0: andre@0: static void _PR_DecrActiveThreadCount(PRThread *thread); andre@0: static PRThread *_PR_AttachThread(PRThreadType, PRThreadPriority, PRThreadStack *); andre@0: static void _PR_InitializeNativeStack(PRThreadStack *ts); andre@0: static void _PR_InitializeRecycledThread(PRThread *thread); andre@0: static void _PR_UserRunThread(void); andre@0: andre@0: void _PR_InitThreads(PRThreadType type, PRThreadPriority priority, andre@0: PRUintn maxPTDs) andre@0: { andre@0: PRThread *thread; andre@0: PRThreadStack *stack; andre@0: andre@0: PR_ASSERT(priority == PR_PRIORITY_NORMAL); andre@0: andre@0: _pr_terminationCVLock = PR_NewLock(); andre@0: _pr_activeLock = PR_NewLock(); andre@0: andre@0: #ifndef HAVE_CUSTOM_USER_THREADS andre@0: stack = PR_NEWZAP(PRThreadStack); andre@0: #ifdef HAVE_STACK_GROWING_UP andre@0: stack->stackTop = (char*) ((((long)&type) >> _pr_pageShift) andre@0: << _pr_pageShift); andre@0: #else andre@0: #if defined(SOLARIS) || defined (UNIXWARE) && defined (USR_SVR4_THREADS) andre@0: stack->stackTop = (char*) &thread; andre@0: #else andre@0: stack->stackTop = (char*) ((((long)&type + _pr_pageSize - 1) andre@0: >> _pr_pageShift) << _pr_pageShift); andre@0: #endif andre@0: #endif andre@0: #else andre@0: /* If stack is NULL, we're using custom user threads like NT fibers. */ andre@0: stack = PR_NEWZAP(PRThreadStack); andre@0: if (stack) { andre@0: stack->stackSize = 0; andre@0: _PR_InitializeNativeStack(stack); andre@0: } andre@0: #endif /* HAVE_CUSTOM_USER_THREADS */ andre@0: andre@0: thread = _PR_AttachThread(type, priority, stack); andre@0: if (thread) { andre@0: _PR_MD_SET_CURRENT_THREAD(thread); andre@0: andre@0: if (type == PR_SYSTEM_THREAD) { andre@0: thread->flags = _PR_SYSTEM; andre@0: _pr_systemActive++; andre@0: _pr_primordialExitCount = 0; andre@0: } else { andre@0: _pr_userActive++; andre@0: _pr_primordialExitCount = 1; andre@0: } andre@0: thread->no_sched = 1; andre@0: _pr_primordialExitCVar = PR_NewCondVar(_pr_activeLock); andre@0: } andre@0: andre@0: if (!thread) PR_Abort(); andre@0: #ifdef _PR_LOCAL_THREADS_ONLY andre@0: thread->flags |= _PR_PRIMORDIAL; andre@0: #else andre@0: thread->flags |= _PR_PRIMORDIAL | _PR_GLOBAL_SCOPE; andre@0: #endif andre@0: andre@0: /* andre@0: * Needs _PR_PRIMORDIAL flag set before calling andre@0: * _PR_MD_INIT_THREAD() andre@0: */ andre@0: if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { andre@0: /* andre@0: * XXX do what? andre@0: */ andre@0: } andre@0: andre@0: if (_PR_IS_NATIVE_THREAD(thread)) { andre@0: PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ()); andre@0: _pr_global_threads++; andre@0: } else { andre@0: PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ()); andre@0: _pr_local_threads++; andre@0: } andre@0: andre@0: _pr_recycleThreads = 0; andre@0: _pr_deadQLock = PR_NewLock(); andre@0: _pr_numNativeDead = 0; andre@0: _pr_numUserDead = 0; andre@0: PR_INIT_CLIST(&_pr_deadNativeQ); andre@0: PR_INIT_CLIST(&_pr_deadUserQ); andre@0: } andre@0: andre@0: void _PR_CleanupThreads(void) andre@0: { andre@0: if (_pr_terminationCVLock) { andre@0: PR_DestroyLock(_pr_terminationCVLock); andre@0: _pr_terminationCVLock = NULL; andre@0: } andre@0: if (_pr_activeLock) { andre@0: PR_DestroyLock(_pr_activeLock); andre@0: _pr_activeLock = NULL; andre@0: } andre@0: if (_pr_primordialExitCVar) { andre@0: PR_DestroyCondVar(_pr_primordialExitCVar); andre@0: _pr_primordialExitCVar = NULL; andre@0: } andre@0: /* TODO _pr_dead{Native,User}Q need to be deleted */ andre@0: if (_pr_deadQLock) { andre@0: PR_DestroyLock(_pr_deadQLock); andre@0: _pr_deadQLock = NULL; andre@0: } andre@0: } andre@0: andre@0: /* andre@0: ** Initialize a stack for a native thread andre@0: */ andre@0: static void _PR_InitializeNativeStack(PRThreadStack *ts) andre@0: { andre@0: if( ts && (ts->stackTop == 0) ) { andre@0: ts->allocSize = ts->stackSize; andre@0: andre@0: /* andre@0: ** Setup stackTop and stackBottom values. andre@0: */ andre@0: #ifdef HAVE_STACK_GROWING_UP andre@0: ts->allocBase = (char*) ((((long)&ts) >> _pr_pageShift) andre@0: << _pr_pageShift); andre@0: ts->stackBottom = ts->allocBase + ts->stackSize; andre@0: ts->stackTop = ts->allocBase; andre@0: #else andre@0: ts->allocBase = (char*) ((((long)&ts + _pr_pageSize - 1) andre@0: >> _pr_pageShift) << _pr_pageShift); andre@0: ts->stackTop = ts->allocBase; andre@0: ts->stackBottom = ts->allocBase - ts->stackSize; andre@0: #endif andre@0: } andre@0: } andre@0: andre@0: void _PR_NotifyJoinWaiters(PRThread *thread) andre@0: { andre@0: /* andre@0: ** Handle joinable threads. Change the state to waiting for join. andre@0: ** Remove from our run Q and put it on global waiting to join Q. andre@0: ** Notify on our "termination" condition variable so that joining andre@0: ** thread will know about our termination. Switch our context and andre@0: ** come back later on to continue the cleanup. andre@0: */ andre@0: PR_ASSERT(thread == _PR_MD_CURRENT_THREAD()); andre@0: if (thread->term != NULL) { andre@0: PR_Lock(_pr_terminationCVLock); andre@0: _PR_THREAD_LOCK(thread); andre@0: thread->state = _PR_JOIN_WAIT; andre@0: if ( !_PR_IS_NATIVE_THREAD(thread) ) { andre@0: _PR_MISCQ_LOCK(thread->cpu); andre@0: _PR_ADD_JOINQ(thread, thread->cpu); andre@0: _PR_MISCQ_UNLOCK(thread->cpu); andre@0: } andre@0: _PR_THREAD_UNLOCK(thread); andre@0: PR_NotifyCondVar(thread->term); andre@0: PR_Unlock(_pr_terminationCVLock); andre@0: _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT); andre@0: PR_ASSERT(thread->state != _PR_JOIN_WAIT); andre@0: } andre@0: andre@0: } andre@0: andre@0: /* andre@0: * Zero some of the data members of a recycled thread. andre@0: * andre@0: * Note that we can do this either when a dead thread is added to andre@0: * the dead thread queue or when it is reused. Here, we are doing andre@0: * this lazily, when the thread is reused in _PR_CreateThread(). andre@0: */ andre@0: static void _PR_InitializeRecycledThread(PRThread *thread) andre@0: { andre@0: /* andre@0: * Assert that the following data members are already zeroed andre@0: * by _PR_CleanupThread(). andre@0: */ andre@0: #ifdef DEBUG andre@0: if (thread->privateData) { andre@0: unsigned int i; andre@0: for (i = 0; i < thread->tpdLength; i++) { andre@0: PR_ASSERT(thread->privateData[i] == NULL); andre@0: } andre@0: } andre@0: #endif andre@0: PR_ASSERT(thread->dumpArg == 0 && thread->dump == 0); andre@0: PR_ASSERT(thread->errorString == 0 && thread->errorStringSize == 0); andre@0: PR_ASSERT(thread->errorStringLength == 0); andre@0: PR_ASSERT(thread->name == 0); andre@0: andre@0: /* Reset data members in thread structure */ andre@0: thread->errorCode = thread->osErrorCode = 0; andre@0: thread->io_pending = thread->io_suspended = PR_FALSE; andre@0: thread->environment = 0; andre@0: PR_INIT_CLIST(&thread->lockList); andre@0: } andre@0: andre@0: PRStatus _PR_RecycleThread(PRThread *thread) andre@0: { andre@0: if ( _PR_IS_NATIVE_THREAD(thread) && andre@0: _PR_NUM_DEADNATIVE < _pr_recycleThreads) { andre@0: _PR_DEADQ_LOCK; andre@0: PR_APPEND_LINK(&thread->links, &_PR_DEADNATIVEQ); andre@0: _PR_INC_DEADNATIVE; andre@0: _PR_DEADQ_UNLOCK; andre@0: return (PR_SUCCESS); andre@0: } else if ( !_PR_IS_NATIVE_THREAD(thread) && andre@0: _PR_NUM_DEADUSER < _pr_recycleThreads) { andre@0: _PR_DEADQ_LOCK; andre@0: PR_APPEND_LINK(&thread->links, &_PR_DEADUSERQ); andre@0: _PR_INC_DEADUSER; andre@0: _PR_DEADQ_UNLOCK; andre@0: return (PR_SUCCESS); andre@0: } andre@0: return (PR_FAILURE); andre@0: } andre@0: andre@0: /* andre@0: * Decrement the active thread count, either _pr_systemActive or andre@0: * _pr_userActive, depending on whether the thread is a system thread andre@0: * or a user thread. If all the user threads, except possibly andre@0: * the primordial thread, have terminated, we notify the primordial andre@0: * thread of this condition. andre@0: * andre@0: * Since this function will lock _pr_activeLock, do not call this andre@0: * function while holding the _pr_activeLock lock, as this will result andre@0: * in a deadlock. andre@0: */ andre@0: andre@0: static void andre@0: _PR_DecrActiveThreadCount(PRThread *thread) andre@0: { andre@0: PR_Lock(_pr_activeLock); andre@0: if (thread->flags & _PR_SYSTEM) { andre@0: _pr_systemActive--; andre@0: } else { andre@0: _pr_userActive--; andre@0: if (_pr_userActive == _pr_primordialExitCount) { andre@0: PR_NotifyCondVar(_pr_primordialExitCVar); andre@0: } andre@0: } andre@0: PR_Unlock(_pr_activeLock); andre@0: } andre@0: andre@0: /* andre@0: ** Detach thread structure andre@0: */ andre@0: static void andre@0: _PR_DestroyThread(PRThread *thread) andre@0: { andre@0: _PR_MD_FREE_LOCK(&thread->threadLock); andre@0: PR_DELETE(thread); andre@0: } andre@0: andre@0: void andre@0: _PR_NativeDestroyThread(PRThread *thread) andre@0: { andre@0: if(thread->term) { andre@0: PR_DestroyCondVar(thread->term); andre@0: thread->term = 0; andre@0: } andre@0: if (NULL != thread->privateData) { andre@0: PR_ASSERT(0 != thread->tpdLength); andre@0: PR_DELETE(thread->privateData); andre@0: thread->tpdLength = 0; andre@0: } andre@0: PR_DELETE(thread->stack); andre@0: _PR_DestroyThread(thread); andre@0: } andre@0: andre@0: void andre@0: _PR_UserDestroyThread(PRThread *thread) andre@0: { andre@0: if(thread->term) { andre@0: PR_DestroyCondVar(thread->term); andre@0: thread->term = 0; andre@0: } andre@0: if (NULL != thread->privateData) { andre@0: PR_ASSERT(0 != thread->tpdLength); andre@0: PR_DELETE(thread->privateData); andre@0: thread->tpdLength = 0; andre@0: } andre@0: _PR_MD_FREE_LOCK(&thread->threadLock); andre@0: if (thread->threadAllocatedOnStack == 1) { andre@0: _PR_MD_CLEAN_THREAD(thread); andre@0: /* andre@0: * Because the no_sched field is set, this thread/stack will andre@0: * will not be re-used until the flag is cleared by the thread andre@0: * we will context switch to. andre@0: */ andre@0: _PR_FreeStack(thread->stack); andre@0: } else { andre@0: #ifdef WINNT andre@0: _PR_MD_CLEAN_THREAD(thread); andre@0: #else andre@0: /* andre@0: * This assertion does not apply to NT. On NT, every fiber andre@0: * has its threadAllocatedOnStack equal to 0. Elsewhere, andre@0: * only the primordial thread has its threadAllocatedOnStack andre@0: * equal to 0. andre@0: */ andre@0: PR_ASSERT(thread->flags & _PR_PRIMORDIAL); andre@0: #endif andre@0: } andre@0: } andre@0: andre@0: andre@0: /* andre@0: ** Run a thread's start function. When the start function returns the andre@0: ** thread is done executing and no longer needs the CPU. If there are no andre@0: ** more user threads running then we can exit the program. andre@0: */ andre@0: void _PR_NativeRunThread(void *arg) andre@0: { andre@0: PRThread *thread = (PRThread *)arg; andre@0: andre@0: _PR_MD_SET_CURRENT_THREAD(thread); andre@0: andre@0: _PR_MD_SET_CURRENT_CPU(NULL); andre@0: andre@0: /* Set up the thread stack information */ andre@0: _PR_InitializeNativeStack(thread->stack); andre@0: andre@0: /* Set up the thread md information */ andre@0: if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { andre@0: /* andre@0: * thread failed to initialize itself, possibly due to andre@0: * failure to allocate per-thread resources andre@0: */ andre@0: return; andre@0: } andre@0: andre@0: while(1) { andre@0: thread->state = _PR_RUNNING; andre@0: andre@0: /* andre@0: * Add to list of active threads andre@0: */ andre@0: PR_Lock(_pr_activeLock); andre@0: PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ()); andre@0: _pr_global_threads++; andre@0: PR_Unlock(_pr_activeLock); andre@0: andre@0: (*thread->startFunc)(thread->arg); andre@0: andre@0: /* andre@0: * The following two assertions are meant for NT asynch io. andre@0: * andre@0: * The thread should have no asynch io in progress when it andre@0: * exits, otherwise the overlapped buffer, which is part of andre@0: * the thread structure, would become invalid. andre@0: */ andre@0: PR_ASSERT(thread->io_pending == PR_FALSE); andre@0: /* andre@0: * This assertion enforces the programming guideline that andre@0: * if an io function times out or is interrupted, the thread andre@0: * should close the fd to force the asynch io to abort andre@0: * before it exits. Right now, closing the fd is the only andre@0: * way to clear the io_suspended flag. andre@0: */ andre@0: PR_ASSERT(thread->io_suspended == PR_FALSE); andre@0: andre@0: /* andre@0: * remove thread from list of active threads andre@0: */ andre@0: PR_Lock(_pr_activeLock); andre@0: PR_REMOVE_LINK(&thread->active); andre@0: _pr_global_threads--; andre@0: PR_Unlock(_pr_activeLock); andre@0: andre@0: PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting")); andre@0: andre@0: /* All done, time to go away */ andre@0: _PR_CleanupThread(thread); andre@0: andre@0: _PR_NotifyJoinWaiters(thread); andre@0: andre@0: _PR_DecrActiveThreadCount(thread); andre@0: andre@0: thread->state = _PR_DEAD_STATE; andre@0: andre@0: if (!_pr_recycleThreads || (_PR_RecycleThread(thread) == andre@0: PR_FAILURE)) { andre@0: /* andre@0: * thread not recycled andre@0: * platform-specific thread exit processing andre@0: * - for stuff like releasing native-thread resources, etc. andre@0: */ andre@0: _PR_MD_EXIT_THREAD(thread); andre@0: /* andre@0: * Free memory allocated for the thread andre@0: */ andre@0: _PR_NativeDestroyThread(thread); andre@0: /* andre@0: * thread gone, cannot de-reference thread now andre@0: */ andre@0: return; andre@0: } andre@0: andre@0: /* Now wait for someone to activate us again... */ andre@0: _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT); andre@0: } andre@0: } andre@0: andre@0: static void _PR_UserRunThread(void) andre@0: { andre@0: PRThread *thread = _PR_MD_CURRENT_THREAD(); andre@0: PRIntn is; andre@0: andre@0: if (_MD_LAST_THREAD()) andre@0: _MD_LAST_THREAD()->no_sched = 0; andre@0: andre@0: #ifdef HAVE_CUSTOM_USER_THREADS andre@0: if (thread->stack == NULL) { andre@0: thread->stack = PR_NEWZAP(PRThreadStack); andre@0: _PR_InitializeNativeStack(thread->stack); andre@0: } andre@0: #endif /* HAVE_CUSTOM_USER_THREADS */ andre@0: andre@0: while(1) { andre@0: /* Run thread main */ andre@0: if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_MD_SET_INTSOFF(0); andre@0: andre@0: /* andre@0: * Add to list of active threads andre@0: */ andre@0: if (!(thread->flags & _PR_IDLE_THREAD)) { andre@0: PR_Lock(_pr_activeLock); andre@0: PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ()); andre@0: _pr_local_threads++; andre@0: PR_Unlock(_pr_activeLock); andre@0: } andre@0: andre@0: (*thread->startFunc)(thread->arg); andre@0: andre@0: /* andre@0: * The following two assertions are meant for NT asynch io. andre@0: * andre@0: * The thread should have no asynch io in progress when it andre@0: * exits, otherwise the overlapped buffer, which is part of andre@0: * the thread structure, would become invalid. andre@0: */ andre@0: PR_ASSERT(thread->io_pending == PR_FALSE); andre@0: /* andre@0: * This assertion enforces the programming guideline that andre@0: * if an io function times out or is interrupted, the thread andre@0: * should close the fd to force the asynch io to abort andre@0: * before it exits. Right now, closing the fd is the only andre@0: * way to clear the io_suspended flag. andre@0: */ andre@0: PR_ASSERT(thread->io_suspended == PR_FALSE); andre@0: andre@0: PR_Lock(_pr_activeLock); andre@0: /* andre@0: * remove thread from list of active threads andre@0: */ andre@0: if (!(thread->flags & _PR_IDLE_THREAD)) { andre@0: PR_REMOVE_LINK(&thread->active); andre@0: _pr_local_threads--; andre@0: } andre@0: PR_Unlock(_pr_activeLock); andre@0: PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting")); andre@0: andre@0: /* All done, time to go away */ andre@0: _PR_CleanupThread(thread); andre@0: andre@0: _PR_INTSOFF(is); andre@0: andre@0: _PR_NotifyJoinWaiters(thread); andre@0: andre@0: _PR_DecrActiveThreadCount(thread); andre@0: andre@0: thread->state = _PR_DEAD_STATE; andre@0: andre@0: if (!_pr_recycleThreads || (_PR_RecycleThread(thread) == andre@0: PR_FAILURE)) { andre@0: /* andre@0: ** Destroy the thread resources andre@0: */ andre@0: _PR_UserDestroyThread(thread); andre@0: } andre@0: andre@0: /* andre@0: ** Find another user thread to run. This cpu has finished the andre@0: ** previous threads main and is now ready to run another thread. andre@0: */ andre@0: { andre@0: PRInt32 is; andre@0: _PR_INTSOFF(is); andre@0: _PR_MD_SWITCH_CONTEXT(thread); andre@0: } andre@0: andre@0: /* Will land here when we get scheduled again if we are recycling... */ andre@0: } andre@0: } andre@0: andre@0: void _PR_SetThreadPriority(PRThread *thread, PRThreadPriority newPri) andre@0: { andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: PRIntn is; andre@0: andre@0: if ( _PR_IS_NATIVE_THREAD(thread) ) { andre@0: _PR_MD_SET_PRIORITY(&(thread->md), newPri); andre@0: return; andre@0: } andre@0: andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSOFF(is); andre@0: _PR_THREAD_LOCK(thread); andre@0: if (newPri != thread->priority) { andre@0: _PRCPU *cpu = thread->cpu; andre@0: andre@0: switch (thread->state) { andre@0: case _PR_RUNNING: andre@0: /* Change my priority */ andre@0: andre@0: _PR_RUNQ_LOCK(cpu); andre@0: thread->priority = newPri; andre@0: if (_PR_RUNQREADYMASK(cpu) >> (newPri + 1)) { andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_SET_RESCHED_FLAG(); andre@0: } andre@0: _PR_RUNQ_UNLOCK(cpu); andre@0: break; andre@0: andre@0: case _PR_RUNNABLE: andre@0: andre@0: _PR_RUNQ_LOCK(cpu); andre@0: /* Move to different runQ */ andre@0: _PR_DEL_RUNQ(thread); andre@0: thread->priority = newPri; andre@0: PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); andre@0: _PR_ADD_RUNQ(thread, cpu, newPri); andre@0: _PR_RUNQ_UNLOCK(cpu); andre@0: andre@0: if (newPri > me->priority) { andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_SET_RESCHED_FLAG(); andre@0: } andre@0: andre@0: break; andre@0: andre@0: case _PR_LOCK_WAIT: andre@0: case _PR_COND_WAIT: andre@0: case _PR_IO_WAIT: andre@0: case _PR_SUSPENDED: andre@0: andre@0: thread->priority = newPri; andre@0: break; andre@0: } andre@0: } andre@0: _PR_THREAD_UNLOCK(thread); andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSON(is); andre@0: } andre@0: andre@0: /* andre@0: ** Suspend the named thread and copy its gc registers into regBuf andre@0: */ andre@0: static void _PR_Suspend(PRThread *thread) andre@0: { andre@0: PRIntn is; andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: andre@0: PR_ASSERT(thread != me); andre@0: PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread) || (!thread->cpu)); andre@0: andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSOFF(is); andre@0: _PR_THREAD_LOCK(thread); andre@0: switch (thread->state) { andre@0: case _PR_RUNNABLE: andre@0: if (!_PR_IS_NATIVE_THREAD(thread)) { andre@0: _PR_RUNQ_LOCK(thread->cpu); andre@0: _PR_DEL_RUNQ(thread); andre@0: _PR_RUNQ_UNLOCK(thread->cpu); andre@0: andre@0: _PR_MISCQ_LOCK(thread->cpu); andre@0: _PR_ADD_SUSPENDQ(thread, thread->cpu); andre@0: _PR_MISCQ_UNLOCK(thread->cpu); andre@0: } else { andre@0: /* andre@0: * Only LOCAL threads are suspended by _PR_Suspend andre@0: */ andre@0: PR_ASSERT(0); andre@0: } andre@0: thread->state = _PR_SUSPENDED; andre@0: break; andre@0: andre@0: case _PR_RUNNING: andre@0: /* andre@0: * The thread being suspended should be a LOCAL thread with andre@0: * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state andre@0: */ andre@0: PR_ASSERT(0); andre@0: break; andre@0: andre@0: case _PR_LOCK_WAIT: andre@0: case _PR_IO_WAIT: andre@0: case _PR_COND_WAIT: andre@0: if (_PR_IS_NATIVE_THREAD(thread)) { andre@0: _PR_MD_SUSPEND_THREAD(thread); andre@0: } andre@0: thread->flags |= _PR_SUSPENDING; andre@0: break; andre@0: andre@0: default: andre@0: PR_Abort(); andre@0: } andre@0: _PR_THREAD_UNLOCK(thread); andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSON(is); andre@0: } andre@0: andre@0: static void _PR_Resume(PRThread *thread) andre@0: { andre@0: PRThreadPriority pri; andre@0: PRIntn is; andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSOFF(is); andre@0: _PR_THREAD_LOCK(thread); andre@0: switch (thread->state) { andre@0: case _PR_SUSPENDED: andre@0: thread->state = _PR_RUNNABLE; andre@0: thread->flags &= ~_PR_SUSPENDING; andre@0: if (!_PR_IS_NATIVE_THREAD(thread)) { andre@0: _PR_MISCQ_LOCK(thread->cpu); andre@0: _PR_DEL_SUSPENDQ(thread); andre@0: _PR_MISCQ_UNLOCK(thread->cpu); andre@0: andre@0: pri = thread->priority; andre@0: andre@0: _PR_RUNQ_LOCK(thread->cpu); andre@0: _PR_ADD_RUNQ(thread, thread->cpu, pri); andre@0: _PR_RUNQ_UNLOCK(thread->cpu); andre@0: andre@0: if (pri > _PR_MD_CURRENT_THREAD()->priority) { andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_SET_RESCHED_FLAG(); andre@0: } andre@0: } else { andre@0: PR_ASSERT(0); andre@0: } andre@0: break; andre@0: andre@0: case _PR_IO_WAIT: andre@0: case _PR_COND_WAIT: andre@0: thread->flags &= ~_PR_SUSPENDING; andre@0: /* PR_ASSERT(thread->wait.monitor->stickyCount == 0); */ andre@0: break; andre@0: andre@0: case _PR_LOCK_WAIT: andre@0: { andre@0: PRLock *wLock = thread->wait.lock; andre@0: andre@0: thread->flags &= ~_PR_SUSPENDING; andre@0: andre@0: _PR_LOCK_LOCK(wLock); andre@0: if (thread->wait.lock->owner == 0) { andre@0: _PR_UnblockLockWaiter(thread->wait.lock); andre@0: } andre@0: _PR_LOCK_UNLOCK(wLock); andre@0: break; andre@0: } andre@0: case _PR_RUNNABLE: andre@0: break; andre@0: case _PR_RUNNING: andre@0: /* andre@0: * The thread being suspended should be a LOCAL thread with andre@0: * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state andre@0: */ andre@0: PR_ASSERT(0); andre@0: break; andre@0: andre@0: default: andre@0: /* andre@0: * thread should have been in one of the above-listed blocked states andre@0: * (_PR_JOIN_WAIT, _PR_IO_WAIT, _PR_UNBORN, _PR_DEAD_STATE) andre@0: */ andre@0: PR_Abort(); andre@0: } andre@0: _PR_THREAD_UNLOCK(thread); andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSON(is); andre@0: andre@0: } andre@0: andre@0: #if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) andre@0: static PRThread *get_thread(_PRCPU *cpu, PRBool *wakeup_cpus) andre@0: { andre@0: PRThread *thread; andre@0: PRIntn pri; andre@0: PRUint32 r; andre@0: PRCList *qp; andre@0: PRIntn priMin, priMax; andre@0: andre@0: _PR_RUNQ_LOCK(cpu); andre@0: r = _PR_RUNQREADYMASK(cpu); andre@0: if (r==0) { andre@0: priMin = priMax = PR_PRIORITY_FIRST; andre@0: } else if (r == (1<= priMin ; pri-- ) { andre@0: if (r & (1 << pri)) { andre@0: for (qp = _PR_RUNQ(cpu)[pri].next; andre@0: qp != &_PR_RUNQ(cpu)[pri]; andre@0: qp = qp->next) { andre@0: thread = _PR_THREAD_PTR(qp); andre@0: /* andre@0: * skip non-schedulable threads andre@0: */ andre@0: PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); andre@0: if (thread->no_sched) { andre@0: thread = NULL; andre@0: /* andre@0: * Need to wakeup cpus to avoid missing a andre@0: * runnable thread andre@0: * Waking up all CPU's need happen only once. andre@0: */ andre@0: andre@0: *wakeup_cpus = PR_TRUE; andre@0: continue; andre@0: } else if (thread->flags & _PR_BOUND_THREAD) { andre@0: /* andre@0: * Thread bound to cpu 0 andre@0: */ andre@0: andre@0: thread = NULL; andre@0: #ifdef IRIX andre@0: _PR_MD_WAKEUP_PRIMORDIAL_CPU(); andre@0: #endif andre@0: continue; andre@0: } else if (thread->io_pending == PR_TRUE) { andre@0: /* andre@0: * A thread that is blocked for I/O needs to run andre@0: * on the same cpu on which it was blocked. This is because andre@0: * the cpu's ioq is accessed without lock protection and scheduling andre@0: * the thread on a different cpu would preclude this optimization. andre@0: */ andre@0: thread = NULL; andre@0: continue; andre@0: } else { andre@0: /* Pull thread off of its run queue */ andre@0: _PR_DEL_RUNQ(thread); andre@0: _PR_RUNQ_UNLOCK(cpu); andre@0: return(thread); andre@0: } andre@0: } andre@0: } andre@0: thread = NULL; andre@0: } andre@0: _PR_RUNQ_UNLOCK(cpu); andre@0: return(thread); andre@0: } andre@0: #endif /* !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) */ andre@0: andre@0: /* andre@0: ** Schedule this native thread by finding the highest priority nspr andre@0: ** thread that is ready to run. andre@0: ** andre@0: ** Note- everyone really needs to call _PR_MD_SWITCH_CONTEXT (which calls andre@0: ** PR_Schedule() rather than calling PR_Schedule. Otherwise if there andre@0: ** is initialization required for switching from SWITCH_CONTEXT, andre@0: ** it will not get done! andre@0: */ andre@0: void _PR_Schedule(void) andre@0: { andre@0: PRThread *thread, *me = _PR_MD_CURRENT_THREAD(); andre@0: _PRCPU *cpu = _PR_MD_CURRENT_CPU(); andre@0: PRIntn pri; andre@0: PRUint32 r; andre@0: PRCList *qp; andre@0: PRIntn priMin, priMax; andre@0: #if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) andre@0: PRBool wakeup_cpus; andre@0: #endif andre@0: andre@0: /* Interrupts must be disabled */ andre@0: PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); andre@0: andre@0: /* Since we are rescheduling, we no longer want to */ andre@0: _PR_CLEAR_RESCHED_FLAG(); andre@0: andre@0: /* andre@0: ** Find highest priority thread to run. Bigger priority numbers are andre@0: ** higher priority threads andre@0: */ andre@0: _PR_RUNQ_LOCK(cpu); andre@0: /* andre@0: * if we are in SuspendAll mode, can schedule only the thread andre@0: * that called PR_SuspendAll andre@0: * andre@0: * The thread may be ready to run now, after completing an I/O andre@0: * operation, for example andre@0: */ andre@0: if ((thread = suspendAllThread) != 0) { andre@0: if ((!(thread->no_sched)) && (thread->state == _PR_RUNNABLE)) { andre@0: /* Pull thread off of its run queue */ andre@0: _PR_DEL_RUNQ(thread); andre@0: _PR_RUNQ_UNLOCK(cpu); andre@0: goto found_thread; andre@0: } else { andre@0: thread = NULL; andre@0: _PR_RUNQ_UNLOCK(cpu); andre@0: goto idle_thread; andre@0: } andre@0: } andre@0: r = _PR_RUNQREADYMASK(cpu); andre@0: if (r==0) { andre@0: priMin = priMax = PR_PRIORITY_FIRST; andre@0: } else if (r == (1<= priMin ; pri-- ) { andre@0: if (r & (1 << pri)) { andre@0: for (qp = _PR_RUNQ(cpu)[pri].next; andre@0: qp != &_PR_RUNQ(cpu)[pri]; andre@0: qp = qp->next) { andre@0: thread = _PR_THREAD_PTR(qp); andre@0: /* andre@0: * skip non-schedulable threads andre@0: */ andre@0: PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); andre@0: if ((thread->no_sched) && (me != thread)){ andre@0: thread = NULL; andre@0: continue; andre@0: } else { andre@0: /* Pull thread off of its run queue */ andre@0: _PR_DEL_RUNQ(thread); andre@0: _PR_RUNQ_UNLOCK(cpu); andre@0: goto found_thread; andre@0: } andre@0: } andre@0: } andre@0: thread = NULL; andre@0: } andre@0: _PR_RUNQ_UNLOCK(cpu); andre@0: andre@0: #if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) andre@0: andre@0: wakeup_cpus = PR_FALSE; andre@0: _PR_CPU_LIST_LOCK(); andre@0: for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) { andre@0: if (cpu != _PR_CPU_PTR(qp)) { andre@0: if ((thread = get_thread(_PR_CPU_PTR(qp), &wakeup_cpus)) andre@0: != NULL) { andre@0: thread->cpu = cpu; andre@0: _PR_CPU_LIST_UNLOCK(); andre@0: if (wakeup_cpus == PR_TRUE) andre@0: _PR_MD_WAKEUP_CPUS(); andre@0: goto found_thread; andre@0: } andre@0: } andre@0: } andre@0: _PR_CPU_LIST_UNLOCK(); andre@0: if (wakeup_cpus == PR_TRUE) andre@0: _PR_MD_WAKEUP_CPUS(); andre@0: andre@0: #endif /* _PR_LOCAL_THREADS_ONLY */ andre@0: andre@0: idle_thread: andre@0: /* andre@0: ** There are no threads to run. Switch to the idle thread andre@0: */ andre@0: PR_LOG(_pr_sched_lm, PR_LOG_MAX, ("pausing")); andre@0: thread = _PR_MD_CURRENT_CPU()->idle_thread; andre@0: andre@0: found_thread: andre@0: PR_ASSERT((me == thread) || ((thread->state == _PR_RUNNABLE) && andre@0: (!(thread->no_sched)))); andre@0: andre@0: /* Resume the thread */ andre@0: PR_LOG(_pr_sched_lm, PR_LOG_MAX, andre@0: ("switching to %d[%p]", thread->id, thread)); andre@0: PR_ASSERT(thread->state != _PR_RUNNING); andre@0: thread->state = _PR_RUNNING; andre@0: andre@0: /* If we are on the runq, it just means that we went to sleep on some andre@0: * resource, and by the time we got here another real native thread had andre@0: * already given us the resource and put us back on the runqueue andre@0: */ andre@0: PR_ASSERT(thread->cpu == _PR_MD_CURRENT_CPU()); andre@0: if (thread != me) andre@0: _PR_MD_RESTORE_CONTEXT(thread); andre@0: #if 0 andre@0: /* XXXMB; with setjmp/longjmp it is impossible to land here, but andre@0: * it is not with fibers... Is this a bad thing? I believe it is andre@0: * still safe. andre@0: */ andre@0: PR_NOT_REACHED("impossible return from schedule"); andre@0: #endif andre@0: } andre@0: andre@0: /* andre@0: ** Attaches a thread. andre@0: ** Does not set the _PR_MD_CURRENT_THREAD. andre@0: ** Does not specify the scope of the thread. andre@0: */ andre@0: static PRThread * andre@0: _PR_AttachThread(PRThreadType type, PRThreadPriority priority, andre@0: PRThreadStack *stack) andre@0: { andre@0: PRThread *thread; andre@0: char *mem; andre@0: andre@0: if (priority > PR_PRIORITY_LAST) { andre@0: priority = PR_PRIORITY_LAST; andre@0: } else if (priority < PR_PRIORITY_FIRST) { andre@0: priority = PR_PRIORITY_FIRST; andre@0: } andre@0: andre@0: mem = (char*) PR_CALLOC(sizeof(PRThread)); andre@0: if (mem) { andre@0: thread = (PRThread*) mem; andre@0: thread->priority = priority; andre@0: thread->stack = stack; andre@0: thread->state = _PR_RUNNING; andre@0: PR_INIT_CLIST(&thread->lockList); andre@0: if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) { andre@0: PR_DELETE(thread); andre@0: return 0; andre@0: } andre@0: andre@0: return thread; andre@0: } andre@0: return 0; andre@0: } andre@0: andre@0: andre@0: andre@0: PR_IMPLEMENT(PRThread*) andre@0: _PR_NativeCreateThread(PRThreadType type, andre@0: void (*start)(void *arg), andre@0: void *arg, andre@0: PRThreadPriority priority, andre@0: PRThreadScope scope, andre@0: PRThreadState state, andre@0: PRUint32 stackSize, andre@0: PRUint32 flags) andre@0: { andre@0: PRThread *thread; andre@0: andre@0: thread = _PR_AttachThread(type, priority, NULL); andre@0: andre@0: if (thread) { andre@0: PR_Lock(_pr_activeLock); andre@0: thread->flags = (flags | _PR_GLOBAL_SCOPE); andre@0: thread->id = ++_pr_utid; andre@0: if (type == PR_SYSTEM_THREAD) { andre@0: thread->flags |= _PR_SYSTEM; andre@0: _pr_systemActive++; andre@0: } else { andre@0: _pr_userActive++; andre@0: } andre@0: PR_Unlock(_pr_activeLock); andre@0: andre@0: thread->stack = PR_NEWZAP(PRThreadStack); andre@0: if (!thread->stack) { andre@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); andre@0: goto done; andre@0: } andre@0: thread->stack->stackSize = stackSize?stackSize:_MD_DEFAULT_STACK_SIZE; andre@0: thread->stack->thr = thread; andre@0: thread->startFunc = start; andre@0: thread->arg = arg; andre@0: andre@0: /* andre@0: Set thread flags related to scope and joinable state. If joinable andre@0: thread, allocate a "termination" conidition variable. andre@0: */ andre@0: if (state == PR_JOINABLE_THREAD) { andre@0: thread->term = PR_NewCondVar(_pr_terminationCVLock); andre@0: if (thread->term == NULL) { andre@0: PR_DELETE(thread->stack); andre@0: goto done; andre@0: } andre@0: } andre@0: andre@0: thread->state = _PR_RUNNING; andre@0: if (_PR_MD_CREATE_THREAD(thread, _PR_NativeRunThread, priority, andre@0: scope,state,stackSize) == PR_SUCCESS) { andre@0: return thread; andre@0: } andre@0: if (thread->term) { andre@0: PR_DestroyCondVar(thread->term); andre@0: thread->term = NULL; andre@0: } andre@0: PR_DELETE(thread->stack); andre@0: } andre@0: andre@0: done: andre@0: if (thread) { andre@0: _PR_DecrActiveThreadCount(thread); andre@0: _PR_DestroyThread(thread); andre@0: } andre@0: return NULL; andre@0: } andre@0: andre@0: /************************************************************************/ andre@0: andre@0: PR_IMPLEMENT(PRThread*) _PR_CreateThread(PRThreadType type, andre@0: void (*start)(void *arg), andre@0: void *arg, andre@0: PRThreadPriority priority, andre@0: PRThreadScope scope, andre@0: PRThreadState state, andre@0: PRUint32 stackSize, andre@0: PRUint32 flags) andre@0: { andre@0: PRThread *me; andre@0: PRThread *thread = NULL; andre@0: PRThreadStack *stack; andre@0: char *top; andre@0: PRIntn is; andre@0: PRIntn native = 0; andre@0: PRIntn useRecycled = 0; andre@0: PRBool status; andre@0: andre@0: /* andre@0: First, pin down the priority. Not all compilers catch passing out of andre@0: range enum here. If we let bad values thru, priority queues won't work. andre@0: */ andre@0: if (priority > PR_PRIORITY_LAST) { andre@0: priority = PR_PRIORITY_LAST; andre@0: } else if (priority < PR_PRIORITY_FIRST) { andre@0: priority = PR_PRIORITY_FIRST; andre@0: } andre@0: andre@0: if (!_pr_initialized) _PR_ImplicitInitialization(); andre@0: andre@0: if (! (flags & _PR_IDLE_THREAD)) andre@0: me = _PR_MD_CURRENT_THREAD(); andre@0: andre@0: #if defined(_PR_GLOBAL_THREADS_ONLY) andre@0: /* andre@0: * can create global threads only andre@0: */ andre@0: if (scope == PR_LOCAL_THREAD) andre@0: scope = PR_GLOBAL_THREAD; andre@0: #endif andre@0: andre@0: if (_native_threads_only) andre@0: scope = PR_GLOBAL_THREAD; andre@0: andre@0: native = (((scope == PR_GLOBAL_THREAD)|| (scope == PR_GLOBAL_BOUND_THREAD)) andre@0: && _PR_IS_NATIVE_THREAD_SUPPORTED()); andre@0: andre@0: _PR_ADJUST_STACKSIZE(stackSize); andre@0: andre@0: if (native) { andre@0: /* andre@0: * clear the IDLE_THREAD flag which applies to LOCAL andre@0: * threads only andre@0: */ andre@0: flags &= ~_PR_IDLE_THREAD; andre@0: flags |= _PR_GLOBAL_SCOPE; andre@0: if (_PR_NUM_DEADNATIVE > 0) { andre@0: _PR_DEADQ_LOCK; andre@0: andre@0: if (_PR_NUM_DEADNATIVE == 0) { /* Thread safe check */ andre@0: _PR_DEADQ_UNLOCK; andre@0: } else { andre@0: thread = _PR_THREAD_PTR(_PR_DEADNATIVEQ.next); andre@0: PR_REMOVE_LINK(&thread->links); andre@0: _PR_DEC_DEADNATIVE; andre@0: _PR_DEADQ_UNLOCK; andre@0: andre@0: _PR_InitializeRecycledThread(thread); andre@0: thread->startFunc = start; andre@0: thread->arg = arg; andre@0: thread->flags = (flags | _PR_GLOBAL_SCOPE); andre@0: if (type == PR_SYSTEM_THREAD) andre@0: { andre@0: thread->flags |= _PR_SYSTEM; andre@0: PR_ATOMIC_INCREMENT(&_pr_systemActive); andre@0: } andre@0: else PR_ATOMIC_INCREMENT(&_pr_userActive); andre@0: andre@0: if (state == PR_JOINABLE_THREAD) { andre@0: if (!thread->term) andre@0: thread->term = PR_NewCondVar(_pr_terminationCVLock); andre@0: } andre@0: else { andre@0: if(thread->term) { andre@0: PR_DestroyCondVar(thread->term); andre@0: thread->term = 0; andre@0: } andre@0: } andre@0: andre@0: thread->priority = priority; andre@0: _PR_MD_SET_PRIORITY(&(thread->md), priority); andre@0: /* XXX what about stackSize? */ andre@0: thread->state = _PR_RUNNING; andre@0: _PR_MD_WAKEUP_WAITER(thread); andre@0: return thread; andre@0: } andre@0: } andre@0: thread = _PR_NativeCreateThread(type, start, arg, priority, andre@0: scope, state, stackSize, flags); andre@0: } else { andre@0: if (_PR_NUM_DEADUSER > 0) { andre@0: _PR_DEADQ_LOCK; andre@0: andre@0: if (_PR_NUM_DEADUSER == 0) { /* thread safe check */ andre@0: _PR_DEADQ_UNLOCK; andre@0: } else { andre@0: PRCList *ptr; andre@0: andre@0: /* Go down list checking for a recycled thread with a andre@0: * large enough stack. XXXMB - this has a bad degenerate case. andre@0: */ andre@0: ptr = _PR_DEADUSERQ.next; andre@0: while( ptr != &_PR_DEADUSERQ ) { andre@0: thread = _PR_THREAD_PTR(ptr); andre@0: if ((thread->stack->stackSize >= stackSize) && andre@0: (!thread->no_sched)) { andre@0: PR_REMOVE_LINK(&thread->links); andre@0: _PR_DEC_DEADUSER; andre@0: break; andre@0: } else { andre@0: ptr = ptr->next; andre@0: thread = NULL; andre@0: } andre@0: } andre@0: andre@0: _PR_DEADQ_UNLOCK; andre@0: andre@0: if (thread) { andre@0: _PR_InitializeRecycledThread(thread); andre@0: thread->startFunc = start; andre@0: thread->arg = arg; andre@0: thread->priority = priority; andre@0: if (state == PR_JOINABLE_THREAD) { andre@0: if (!thread->term) andre@0: thread->term = PR_NewCondVar(_pr_terminationCVLock); andre@0: } else { andre@0: if(thread->term) { andre@0: PR_DestroyCondVar(thread->term); andre@0: thread->term = 0; andre@0: } andre@0: } andre@0: useRecycled++; andre@0: } andre@0: } andre@0: } andre@0: if (thread == NULL) { andre@0: #ifndef HAVE_CUSTOM_USER_THREADS andre@0: stack = _PR_NewStack(stackSize); andre@0: if (!stack) { andre@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); andre@0: return NULL; andre@0: } andre@0: andre@0: /* Allocate thread object and per-thread data off the top of the stack*/ andre@0: top = stack->stackTop; andre@0: #ifdef HAVE_STACK_GROWING_UP andre@0: thread = (PRThread*) top; andre@0: top = top + sizeof(PRThread); andre@0: /* andre@0: * Make stack 64-byte aligned andre@0: */ andre@0: if ((PRUptrdiff)top & 0x3f) { andre@0: top = (char*)(((PRUptrdiff)top + 0x40) & ~0x3f); andre@0: } andre@0: #else andre@0: top = top - sizeof(PRThread); andre@0: thread = (PRThread*) top; andre@0: /* andre@0: * Make stack 64-byte aligned andre@0: */ andre@0: if ((PRUptrdiff)top & 0x3f) { andre@0: top = (char*)((PRUptrdiff)top & ~0x3f); andre@0: } andre@0: #endif andre@0: stack->thr = thread; andre@0: memset(thread, 0, sizeof(PRThread)); andre@0: thread->threadAllocatedOnStack = 1; andre@0: #else andre@0: thread = _PR_MD_CREATE_USER_THREAD(stackSize, start, arg); andre@0: if (!thread) { andre@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); andre@0: return NULL; andre@0: } andre@0: thread->threadAllocatedOnStack = 0; andre@0: stack = NULL; andre@0: top = NULL; andre@0: #endif andre@0: andre@0: /* Initialize thread */ andre@0: thread->tpdLength = 0; andre@0: thread->privateData = NULL; andre@0: thread->stack = stack; andre@0: thread->priority = priority; andre@0: thread->startFunc = start; andre@0: thread->arg = arg; andre@0: PR_INIT_CLIST(&thread->lockList); andre@0: andre@0: if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { andre@0: if (thread->threadAllocatedOnStack == 1) andre@0: _PR_FreeStack(thread->stack); andre@0: else { andre@0: PR_DELETE(thread); andre@0: } andre@0: PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); andre@0: return NULL; andre@0: } andre@0: andre@0: if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) { andre@0: if (thread->threadAllocatedOnStack == 1) andre@0: _PR_FreeStack(thread->stack); andre@0: else { andre@0: PR_DELETE(thread->privateData); andre@0: PR_DELETE(thread); andre@0: } andre@0: PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); andre@0: return NULL; andre@0: } andre@0: andre@0: _PR_MD_INIT_CONTEXT(thread, top, _PR_UserRunThread, &status); andre@0: andre@0: if (status == PR_FALSE) { andre@0: _PR_MD_FREE_LOCK(&thread->threadLock); andre@0: if (thread->threadAllocatedOnStack == 1) andre@0: _PR_FreeStack(thread->stack); andre@0: else { andre@0: PR_DELETE(thread->privateData); andre@0: PR_DELETE(thread); andre@0: } andre@0: return NULL; andre@0: } andre@0: andre@0: /* andre@0: Set thread flags related to scope and joinable state. If joinable andre@0: thread, allocate a "termination" condition variable. andre@0: */ andre@0: if (state == PR_JOINABLE_THREAD) { andre@0: thread->term = PR_NewCondVar(_pr_terminationCVLock); andre@0: if (thread->term == NULL) { andre@0: _PR_MD_FREE_LOCK(&thread->threadLock); andre@0: if (thread->threadAllocatedOnStack == 1) andre@0: _PR_FreeStack(thread->stack); andre@0: else { andre@0: PR_DELETE(thread->privateData); andre@0: PR_DELETE(thread); andre@0: } andre@0: return NULL; andre@0: } andre@0: } andre@0: andre@0: } andre@0: andre@0: /* Update thread type counter */ andre@0: PR_Lock(_pr_activeLock); andre@0: thread->flags = flags; andre@0: thread->id = ++_pr_utid; andre@0: if (type == PR_SYSTEM_THREAD) { andre@0: thread->flags |= _PR_SYSTEM; andre@0: _pr_systemActive++; andre@0: } else { andre@0: _pr_userActive++; andre@0: } andre@0: andre@0: /* Make thread runnable */ andre@0: thread->state = _PR_RUNNABLE; andre@0: /* andre@0: * Add to list of active threads andre@0: */ andre@0: PR_Unlock(_pr_activeLock); andre@0: andre@0: if ((! (thread->flags & _PR_IDLE_THREAD)) && _PR_IS_NATIVE_THREAD(me) ) andre@0: thread->cpu = _PR_GetPrimordialCPU(); andre@0: else andre@0: thread->cpu = _PR_MD_CURRENT_CPU(); andre@0: andre@0: PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread)); andre@0: andre@0: if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me)) { andre@0: _PR_INTSOFF(is); andre@0: _PR_RUNQ_LOCK(thread->cpu); andre@0: _PR_ADD_RUNQ(thread, thread->cpu, priority); andre@0: _PR_RUNQ_UNLOCK(thread->cpu); andre@0: } andre@0: andre@0: if (thread->flags & _PR_IDLE_THREAD) { andre@0: /* andre@0: ** If the creating thread is a kernel thread, we need to andre@0: ** awaken the user thread idle thread somehow; potentially andre@0: ** it could be sleeping in its idle loop, and we need to poke andre@0: ** it. To do so, wake the idle thread... andre@0: */ andre@0: _PR_MD_WAKEUP_WAITER(NULL); andre@0: } else if (_PR_IS_NATIVE_THREAD(me)) { andre@0: _PR_MD_WAKEUP_WAITER(thread); andre@0: } andre@0: if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me) ) andre@0: _PR_INTSON(is); andre@0: } andre@0: andre@0: return thread; andre@0: } andre@0: andre@0: PR_IMPLEMENT(PRThread*) PR_CreateThread(PRThreadType type, andre@0: void (*start)(void *arg), andre@0: void *arg, andre@0: PRThreadPriority priority, andre@0: PRThreadScope scope, andre@0: PRThreadState state, andre@0: PRUint32 stackSize) andre@0: { andre@0: return _PR_CreateThread(type, start, arg, priority, scope, state, andre@0: stackSize, 0); andre@0: } andre@0: andre@0: /* andre@0: ** Associate a thread object with an existing native thread. andre@0: ** "type" is the type of thread object to attach andre@0: ** "priority" is the priority to assign to the thread andre@0: ** "stack" defines the shape of the threads stack andre@0: ** andre@0: ** This can return NULL if some kind of error occurs, or if memory is andre@0: ** tight. andre@0: ** andre@0: ** This call is not normally needed unless you create your own native andre@0: ** thread. PR_Init does this automatically for the primordial thread. andre@0: */ andre@0: PRThread* _PRI_AttachThread(PRThreadType type, andre@0: PRThreadPriority priority, PRThreadStack *stack, PRUint32 flags) andre@0: { andre@0: PRThread *thread; andre@0: andre@0: if ((thread = _PR_MD_GET_ATTACHED_THREAD()) != NULL) { andre@0: return thread; andre@0: } andre@0: _PR_MD_SET_CURRENT_THREAD(NULL); andre@0: andre@0: /* Clear out any state if this thread was attached before */ andre@0: _PR_MD_SET_CURRENT_CPU(NULL); andre@0: andre@0: thread = _PR_AttachThread(type, priority, stack); andre@0: if (thread) { andre@0: PRIntn is; andre@0: andre@0: _PR_MD_SET_CURRENT_THREAD(thread); andre@0: andre@0: thread->flags = flags | _PR_GLOBAL_SCOPE | _PR_ATTACHED; andre@0: andre@0: if (!stack) { andre@0: thread->stack = PR_NEWZAP(PRThreadStack); andre@0: if (!thread->stack) { andre@0: _PR_DestroyThread(thread); andre@0: return NULL; andre@0: } andre@0: thread->stack->stackSize = _MD_DEFAULT_STACK_SIZE; andre@0: } andre@0: PR_INIT_CLIST(&thread->links); andre@0: andre@0: if (_PR_MD_INIT_ATTACHED_THREAD(thread) == PR_FAILURE) { andre@0: PR_DELETE(thread->stack); andre@0: _PR_DestroyThread(thread); andre@0: return NULL; andre@0: } andre@0: andre@0: _PR_MD_SET_CURRENT_CPU(NULL); andre@0: andre@0: if (_PR_MD_CURRENT_CPU()) { andre@0: _PR_INTSOFF(is); andre@0: PR_Lock(_pr_activeLock); andre@0: } andre@0: if (type == PR_SYSTEM_THREAD) { andre@0: thread->flags |= _PR_SYSTEM; andre@0: _pr_systemActive++; andre@0: } else { andre@0: _pr_userActive++; andre@0: } andre@0: if (_PR_MD_CURRENT_CPU()) { andre@0: PR_Unlock(_pr_activeLock); andre@0: _PR_INTSON(is); andre@0: } andre@0: } andre@0: return thread; andre@0: } andre@0: andre@0: PR_IMPLEMENT(PRThread*) PR_AttachThread(PRThreadType type, andre@0: PRThreadPriority priority, PRThreadStack *stack) andre@0: { andre@0: return PR_GetCurrentThread(); andre@0: } andre@0: andre@0: PR_IMPLEMENT(void) PR_DetachThread(void) andre@0: { andre@0: /* andre@0: * On IRIX, Solaris, and Windows, foreign threads are detached when andre@0: * they terminate. andre@0: */ andre@0: #if !defined(IRIX) && !defined(WIN32) \ andre@0: && !(defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)) andre@0: PRThread *me; andre@0: if (_pr_initialized) { andre@0: me = _PR_MD_GET_ATTACHED_THREAD(); andre@0: if ((me != NULL) && (me->flags & _PR_ATTACHED)) andre@0: _PRI_DetachThread(); andre@0: } andre@0: #endif andre@0: } andre@0: andre@0: void _PRI_DetachThread(void) andre@0: { andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: andre@0: if (me->flags & _PR_PRIMORDIAL) { andre@0: /* andre@0: * ignore, if primordial thread andre@0: */ andre@0: return; andre@0: } andre@0: PR_ASSERT(me->flags & _PR_ATTACHED); andre@0: PR_ASSERT(_PR_IS_NATIVE_THREAD(me)); andre@0: _PR_CleanupThread(me); andre@0: PR_DELETE(me->privateData); andre@0: andre@0: _PR_DecrActiveThreadCount(me); andre@0: andre@0: _PR_MD_CLEAN_THREAD(me); andre@0: _PR_MD_SET_CURRENT_THREAD(NULL); andre@0: if (!me->threadAllocatedOnStack) andre@0: PR_DELETE(me->stack); andre@0: _PR_MD_FREE_LOCK(&me->threadLock); andre@0: PR_DELETE(me); andre@0: } andre@0: andre@0: /* andre@0: ** Wait for thread termination: andre@0: ** "thread" is the target thread andre@0: ** andre@0: ** This can return PR_FAILURE if no joinable thread could be found andre@0: ** corresponding to the specified target thread. andre@0: ** andre@0: ** The calling thread is suspended until the target thread completes. andre@0: ** Several threads cannot wait for the same thread to complete; one thread andre@0: ** will complete successfully and others will terminate with an error PR_FAILURE. andre@0: ** The calling thread will not be blocked if the target thread has already andre@0: ** terminated. andre@0: */ andre@0: PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thread) andre@0: { andre@0: PRIntn is; andre@0: PRCondVar *term; andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSOFF(is); andre@0: term = thread->term; andre@0: /* can't join a non-joinable thread */ andre@0: if (term == NULL) { andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: goto ErrorExit; andre@0: } andre@0: andre@0: /* multiple threads can't wait on the same joinable thread */ andre@0: if (term->condQ.next != &term->condQ) { andre@0: goto ErrorExit; andre@0: } andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSON(is); andre@0: andre@0: /* wait for the target thread's termination cv invariant */ andre@0: PR_Lock (_pr_terminationCVLock); andre@0: while (thread->state != _PR_JOIN_WAIT) { andre@0: (void) PR_WaitCondVar(term, PR_INTERVAL_NO_TIMEOUT); andre@0: } andre@0: (void) PR_Unlock (_pr_terminationCVLock); andre@0: andre@0: /* andre@0: Remove target thread from global waiting to join Q; make it runnable andre@0: again and put it back on its run Q. When it gets scheduled later in andre@0: _PR_RunThread code, it will clean up its stack. andre@0: */ andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSOFF(is); andre@0: thread->state = _PR_RUNNABLE; andre@0: if ( !_PR_IS_NATIVE_THREAD(thread) ) { andre@0: _PR_THREAD_LOCK(thread); andre@0: andre@0: _PR_MISCQ_LOCK(thread->cpu); andre@0: _PR_DEL_JOINQ(thread); andre@0: _PR_MISCQ_UNLOCK(thread->cpu); andre@0: andre@0: _PR_AddThreadToRunQ(me, thread); andre@0: _PR_THREAD_UNLOCK(thread); andre@0: } andre@0: if (!_PR_IS_NATIVE_THREAD(me)) andre@0: _PR_INTSON(is); andre@0: andre@0: _PR_MD_WAKEUP_WAITER(thread); andre@0: andre@0: return PR_SUCCESS; andre@0: andre@0: ErrorExit: andre@0: if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thread, andre@0: PRThreadPriority newPri) andre@0: { andre@0: andre@0: /* andre@0: First, pin down the priority. Not all compilers catch passing out of andre@0: range enum here. If we let bad values thru, priority queues won't work. andre@0: */ andre@0: if ((PRIntn)newPri > (PRIntn)PR_PRIORITY_LAST) { andre@0: newPri = PR_PRIORITY_LAST; andre@0: } else if ((PRIntn)newPri < (PRIntn)PR_PRIORITY_FIRST) { andre@0: newPri = PR_PRIORITY_FIRST; andre@0: } andre@0: andre@0: if ( _PR_IS_NATIVE_THREAD(thread) ) { andre@0: thread->priority = newPri; andre@0: _PR_MD_SET_PRIORITY(&(thread->md), newPri); andre@0: } else _PR_SetThreadPriority(thread, newPri); andre@0: } andre@0: andre@0: PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char *name) andre@0: { andre@0: PRThread *thread; andre@0: size_t nameLen; andre@0: andre@0: if (!name) { andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: thread = PR_GetCurrentThread(); andre@0: if (!thread) andre@0: return PR_FAILURE; andre@0: andre@0: PR_Free(thread->name); andre@0: nameLen = strlen(name); andre@0: thread->name = (char *)PR_Malloc(nameLen + 1); andre@0: if (!thread->name) andre@0: return PR_FAILURE; andre@0: memcpy(thread->name, name, nameLen + 1); andre@0: _PR_MD_SET_CURRENT_THREAD_NAME(thread->name); andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread) andre@0: { andre@0: if (!thread) andre@0: return NULL; andre@0: return thread->name; andre@0: } andre@0: andre@0: andre@0: /* andre@0: ** This routine prevents all other threads from running. This call is needed by andre@0: ** the garbage collector. andre@0: */ andre@0: PR_IMPLEMENT(void) PR_SuspendAll(void) andre@0: { andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: PRCList *qp; andre@0: andre@0: /* andre@0: * Stop all user and native threads which are marked GC able. andre@0: */ andre@0: PR_Lock(_pr_activeLock); andre@0: suspendAllOn = PR_TRUE; andre@0: suspendAllThread = _PR_MD_CURRENT_THREAD(); andre@0: _PR_MD_BEGIN_SUSPEND_ALL(); andre@0: for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; andre@0: qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) { andre@0: if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && andre@0: _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) { andre@0: _PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); andre@0: PR_ASSERT((_PR_ACTIVE_THREAD_PTR(qp))->state != _PR_RUNNING); andre@0: } andre@0: } andre@0: for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; andre@0: qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) { andre@0: if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && andre@0: _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) andre@0: /* PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); */ andre@0: _PR_MD_SUSPEND_THREAD(_PR_ACTIVE_THREAD_PTR(qp)); andre@0: } andre@0: _PR_MD_END_SUSPEND_ALL(); andre@0: } andre@0: andre@0: /* andre@0: ** This routine unblocks all other threads that were suspended from running by andre@0: ** PR_SuspendAll(). This call is needed by the garbage collector. andre@0: */ andre@0: PR_IMPLEMENT(void) PR_ResumeAll(void) andre@0: { andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: PRCList *qp; andre@0: andre@0: /* andre@0: * Resume all user and native threads which are marked GC able. andre@0: */ andre@0: _PR_MD_BEGIN_RESUME_ALL(); andre@0: for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; andre@0: qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) { andre@0: if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && andre@0: _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) andre@0: _PR_Resume(_PR_ACTIVE_THREAD_PTR(qp)); andre@0: } andre@0: for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; andre@0: qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) { andre@0: if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && andre@0: _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) andre@0: _PR_MD_RESUME_THREAD(_PR_ACTIVE_THREAD_PTR(qp)); andre@0: } andre@0: _PR_MD_END_RESUME_ALL(); andre@0: suspendAllThread = NULL; andre@0: suspendAllOn = PR_FALSE; andre@0: PR_Unlock(_pr_activeLock); andre@0: } andre@0: andre@0: PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg) andre@0: { andre@0: PRCList *qp, *qp_next; andre@0: PRIntn i = 0; andre@0: PRStatus rv = PR_SUCCESS; andre@0: PRThread* t; andre@0: andre@0: /* andre@0: ** Currently Enumerate threads happen only with suspension and andre@0: ** pr_activeLock held andre@0: */ andre@0: PR_ASSERT(suspendAllOn); andre@0: andre@0: /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking andre@0: * qp->next after applying the function "func". In particular, "func" andre@0: * might remove the thread from the queue and put it into another one in andre@0: * which case qp->next no longer points to the next entry in the original andre@0: * queue. andre@0: * andre@0: * To get around this problem, we save qp->next in qp_next before applying andre@0: * "func" and use that saved value as the next value after applying "func". andre@0: */ andre@0: andre@0: /* andre@0: * Traverse the list of local and global threads andre@0: */ andre@0: for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; andre@0: qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp_next) andre@0: { andre@0: qp_next = qp->next; andre@0: t = _PR_ACTIVE_THREAD_PTR(qp); andre@0: if (_PR_IS_GCABLE_THREAD(t)) andre@0: { andre@0: rv = (*func)(t, i, arg); andre@0: if (rv != PR_SUCCESS) andre@0: return rv; andre@0: i++; andre@0: } andre@0: } andre@0: for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; andre@0: qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp_next) andre@0: { andre@0: qp_next = qp->next; andre@0: t = _PR_ACTIVE_THREAD_PTR(qp); andre@0: if (_PR_IS_GCABLE_THREAD(t)) andre@0: { andre@0: rv = (*func)(t, i, arg); andre@0: if (rv != PR_SUCCESS) andre@0: return rv; andre@0: i++; andre@0: } andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: /* FUNCTION: _PR_AddSleepQ andre@0: ** DESCRIPTION: andre@0: ** Adds a thread to the sleep/pauseQ. andre@0: ** RESTRICTIONS: andre@0: ** Caller must have the RUNQ lock. andre@0: ** Caller must be a user level thread andre@0: */ andre@0: PR_IMPLEMENT(void) andre@0: _PR_AddSleepQ(PRThread *thread, PRIntervalTime timeout) andre@0: { andre@0: _PRCPU *cpu = thread->cpu; andre@0: andre@0: if (timeout == PR_INTERVAL_NO_TIMEOUT) { andre@0: /* append the thread to the global pause Q */ andre@0: PR_APPEND_LINK(&thread->links, &_PR_PAUSEQ(thread->cpu)); andre@0: thread->flags |= _PR_ON_PAUSEQ; andre@0: } else { andre@0: PRIntervalTime sleep; andre@0: PRCList *q; andre@0: PRThread *t; andre@0: andre@0: /* sort onto global sleepQ */ andre@0: sleep = timeout; andre@0: andre@0: /* Check if we are longest timeout */ andre@0: if (timeout >= _PR_SLEEPQMAX(cpu)) { andre@0: PR_INSERT_BEFORE(&thread->links, &_PR_SLEEPQ(cpu)); andre@0: thread->sleep = timeout - _PR_SLEEPQMAX(cpu); andre@0: _PR_SLEEPQMAX(cpu) = timeout; andre@0: } else { andre@0: /* Sort thread into global sleepQ at appropriate point */ andre@0: q = _PR_SLEEPQ(cpu).next; andre@0: andre@0: /* Now scan the list for where to insert this entry */ andre@0: while (q != &_PR_SLEEPQ(cpu)) { andre@0: t = _PR_THREAD_PTR(q); andre@0: if (sleep < t->sleep) { andre@0: /* Found sleeper to insert in front of */ andre@0: break; andre@0: } andre@0: sleep -= t->sleep; andre@0: q = q->next; andre@0: } andre@0: thread->sleep = sleep; andre@0: PR_INSERT_BEFORE(&thread->links, q); andre@0: andre@0: /* andre@0: ** Subtract our sleep time from the sleeper that follows us (there andre@0: ** must be one) so that they remain relative to us. andre@0: */ andre@0: PR_ASSERT (thread->links.next != &_PR_SLEEPQ(cpu)); andre@0: andre@0: t = _PR_THREAD_PTR(thread->links.next); andre@0: PR_ASSERT(_PR_THREAD_PTR(t->links.prev) == thread); andre@0: t->sleep -= sleep; andre@0: } andre@0: andre@0: thread->flags |= _PR_ON_SLEEPQ; andre@0: } andre@0: } andre@0: andre@0: /* FUNCTION: _PR_DelSleepQ andre@0: ** DESCRIPTION: andre@0: ** Removes a thread from the sleep/pauseQ. andre@0: ** INPUTS: andre@0: ** If propogate_time is true, then the thread following the deleted andre@0: ** thread will be get the time from the deleted thread. This is used andre@0: ** when deleting a sleeper that has not timed out. andre@0: ** RESTRICTIONS: andre@0: ** Caller must have the RUNQ lock. andre@0: ** Caller must be a user level thread andre@0: */ andre@0: PR_IMPLEMENT(void) andre@0: _PR_DelSleepQ(PRThread *thread, PRBool propogate_time) andre@0: { andre@0: _PRCPU *cpu = thread->cpu; andre@0: andre@0: /* Remove from pauseQ/sleepQ */ andre@0: if (thread->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) { andre@0: if (thread->flags & _PR_ON_SLEEPQ) { andre@0: PRCList *q = thread->links.next; andre@0: if (q != &_PR_SLEEPQ(cpu)) { andre@0: if (propogate_time == PR_TRUE) { andre@0: PRThread *after = _PR_THREAD_PTR(q); andre@0: after->sleep += thread->sleep; andre@0: } else andre@0: _PR_SLEEPQMAX(cpu) -= thread->sleep; andre@0: } else { andre@0: /* Check if prev is the beggining of the list; if so, andre@0: * we are the only element on the list. andre@0: */ andre@0: if (thread->links.prev != &_PR_SLEEPQ(cpu)) andre@0: _PR_SLEEPQMAX(cpu) -= thread->sleep; andre@0: else andre@0: _PR_SLEEPQMAX(cpu) = 0; andre@0: } andre@0: thread->flags &= ~_PR_ON_SLEEPQ; andre@0: } else { andre@0: thread->flags &= ~_PR_ON_PAUSEQ; andre@0: } andre@0: PR_REMOVE_LINK(&thread->links); andre@0: } else andre@0: PR_ASSERT(0); andre@0: } andre@0: andre@0: void andre@0: _PR_AddThreadToRunQ( andre@0: PRThread *me, /* the current thread */ andre@0: PRThread *thread) /* the local thread to be added to a run queue */ andre@0: { andre@0: PRThreadPriority pri = thread->priority; andre@0: _PRCPU *cpu = thread->cpu; andre@0: andre@0: PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread)); andre@0: andre@0: #if defined(WINNT) andre@0: /* andre@0: * On NT, we can only reliably know that the current CPU andre@0: * is not idle. We add the awakened thread to the run andre@0: * queue of its CPU if its CPU is the current CPU. andre@0: * For any other CPU, we don't really know whether it andre@0: * is busy or idle. So in all other cases, we just andre@0: * "post" the awakened thread to the IO completion port andre@0: * for the next idle CPU to execute (this is done in andre@0: * _PR_MD_WAKEUP_WAITER). andre@0: * Threads with a suspended I/O operation remain bound to andre@0: * the same cpu until I/O is cancelled andre@0: * andre@0: * NOTE: the boolean expression below must be the exact andre@0: * opposite of the corresponding boolean expression in andre@0: * _PR_MD_WAKEUP_WAITER. andre@0: */ andre@0: if ((!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) || andre@0: (thread->md.thr_bound_cpu)) { andre@0: PR_ASSERT(!thread->md.thr_bound_cpu || andre@0: (thread->md.thr_bound_cpu == cpu)); andre@0: _PR_RUNQ_LOCK(cpu); andre@0: _PR_ADD_RUNQ(thread, cpu, pri); andre@0: _PR_RUNQ_UNLOCK(cpu); andre@0: } andre@0: #else andre@0: _PR_RUNQ_LOCK(cpu); andre@0: _PR_ADD_RUNQ(thread, cpu, pri); andre@0: _PR_RUNQ_UNLOCK(cpu); andre@0: if (!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) { andre@0: if (pri > me->priority) { andre@0: _PR_SET_RESCHED_FLAG(); andre@0: } andre@0: } andre@0: #endif andre@0: }