diff nspr/pr/src/threads/combined/pruthr.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nspr/pr/src/threads/combined/pruthr.c	Mon Jul 28 10:47:06 2014 +0200
@@ -0,0 +1,1889 @@
+/* -*- 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_ASSERT(priority == PR_PRIORITY_NORMAL);
+
+    _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
+}
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)