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