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