summaryrefslogtreecommitdiffstats
path: root/nsprpub/pr/src/threads/combined
diff options
context:
space:
mode:
Diffstat (limited to 'nsprpub/pr/src/threads/combined')
-rw-r--r--nsprpub/pr/src/threads/combined/Makefile.in40
-rw-r--r--nsprpub/pr/src/threads/combined/README62
-rw-r--r--nsprpub/pr/src/threads/combined/prucpu.c424
-rw-r--r--nsprpub/pr/src/threads/combined/prucv.c679
-rw-r--r--nsprpub/pr/src/threads/combined/prulock.c457
-rw-r--r--nsprpub/pr/src/threads/combined/prustack.c175
-rw-r--r--nsprpub/pr/src/threads/combined/pruthr.c1942
7 files changed, 3779 insertions, 0 deletions
diff --git a/nsprpub/pr/src/threads/combined/Makefile.in b/nsprpub/pr/src/threads/combined/Makefile.in
new file mode 100644
index 0000000000..14b9eac007
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/Makefile.in
@@ -0,0 +1,40 @@
+#
+# 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/.
+
+
+#! gmake
+
+MOD_DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+ifdef USE_PTHREADS
+CSRCS = \
+ $(NULL)
+else
+CSRCS = \
+ prucpu.c \
+ prucv.c \
+ prulock.c \
+ pruthr.c \
+ prustack.c \
+ $(NULL)
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
diff --git a/nsprpub/pr/src/threads/combined/README b/nsprpub/pr/src/threads/combined/README
new file mode 100644
index 0000000000..aa26665282
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/README
@@ -0,0 +1,62 @@
+NSPR 2.0 evolution
+------------------
+
+
+Phase 1- today
+
+Currently (Oct 10, 1996) NSPR 2.0 has two modes. Either _PR_NTHREAD
+is defined, in which case the PR_CreateThread() call always creates a
+native kernel thread, or _PR_NTHREAD is not defined and PR_CreateThread()
+always creates user level threads within the single, original process. This
+source code is reflected in two directories, nspr20/pr/src/threads/native, and
+nspr20/pr/src/threads/user. Although the PR_CreateThread() function has
+a paramter to specify the "scope" of a thread, this parameter is not yet
+used- except on solaris where it uses it to specify bound vs unbound threads.
+
+Phase 2 - next week
+
+The next step is to provide a combination of user and native threads. The
+idea, of course, is to have some small number of native threads and each of
+those threads be able to run user level threads. The number of native
+threads created will most likely be proportional to the number of CPUs in
+the system. For this reason, the specific set of native threads which are
+used to run the user-level threads will be called "CPU" threads.
+
+The user level threads which will be run on the CPU threads are able to
+run on any of the CPU threads available, and over the course of a user-level
+thread's lifetime, it may drift from one CPU thread to another. All
+user-level threads will compete for processing time via a single run queue.
+
+Creation of a CPU thread will be primarily controlled by NSPR itself or by
+the user running a function PR_Concurrency(). The details of PR_Concurrency()
+have not yet been worked out; but the idea is that the user can specify to
+NSPR how many CPU threads are desired.
+
+In this system, user-level threads are created by using PR_CreateThread() and
+specifying the PR_LOCAL_SCOPE option. LOCAL_SCOPE indicates that the thread
+will be under the control of the "local" scheduler. Creating threads with
+GLOBAL_SCOPE, on the other hand will create a thread which is under the
+control of the system's scheduler. In otherwords, this creates a native thread
+which is not a CPU thread; it runs a single thread task and never has more
+than one task to run. LOCAL_SCOPE is much like creating a Solaris unbound
+thread, while GLOBAL_SCOPE is similar to creating a Solaris bound thread.
+
+To implement this architecture, the source code will still maintain the "user"
+and "native" directories which is has today. However a third directory
+"combined" will also exist. To compile a version of NSPR which only creates
+native threads, the user can define _PR_NTHREAD. For exclusive user-level
+threads, do not define _PR_NTHREAD. To get the combined threads, define
+_PR_NTHREAD and _PR_USE_CPUS.
+
+
+Phase 3 - later than next week
+
+The goal is to eliminate the 3 directories. Once these three models are in
+place, the remaining work will be to eliminate the native and user thread
+directories for all platforms, so that the entire thread model is contained
+within what is today called the "combined" model. This new and glorious
+source code will attempt to make the "combined" model on any platforms which
+provide the necessary underlying native threading, but will also be
+capable of using exclusive user-level threads on systems which don't have
+native threads.
+
diff --git a/nsprpub/pr/src/threads/combined/prucpu.c b/nsprpub/pr/src/threads/combined/prucpu.c
new file mode 100644
index 0000000000..25ffcce862
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/prucpu.c
@@ -0,0 +1,424 @@
+/* -*- 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"
+
+_PRCPU *_pr_primordialCPU = NULL;
+
+PRInt32 _pr_md_idle_cpus; /* number of idle cpus */
+/*
+ * The idle threads in MxN models increment/decrement _pr_md_idle_cpus.
+ * If _PR_HAVE_ATOMIC_OPS is not defined, they can't use the atomic
+ * increment/decrement routines (which are based on PR_Lock/PR_Unlock),
+ * because PR_Lock asserts that the calling thread is not an idle thread.
+ * So we use a _MDLock to protect _pr_md_idle_cpus.
+ */
+#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
+#ifndef _PR_HAVE_ATOMIC_OPS
+static _MDLock _pr_md_idle_cpus_lock;
+#endif
+#endif
+PRUintn _pr_numCPU;
+PRInt32 _pr_cpus_exit;
+PRUint32 _pr_cpu_affinity_mask = 0;
+
+#if !defined (_PR_GLOBAL_THREADS_ONLY)
+
+static PRUintn _pr_cpuID;
+
+static void PR_CALLBACK _PR_CPU_Idle(void *);
+
+static _PRCPU *_PR_CreateCPU(void);
+static PRStatus _PR_StartCPU(_PRCPU *cpu, PRThread *thread);
+
+#if !defined(_PR_LOCAL_THREADS_ONLY)
+static void _PR_RunCPU(void *arg);
+#endif
+
+void _PR_InitCPUs()
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_native_threads_only) {
+ return;
+ }
+
+ _pr_cpuID = 0;
+ _MD_NEW_LOCK( &_pr_cpuLock);
+#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
+#ifndef _PR_HAVE_ATOMIC_OPS
+ _MD_NEW_LOCK(&_pr_md_idle_cpus_lock);
+#endif
+#endif
+
+#ifdef _PR_LOCAL_THREADS_ONLY
+
+#ifdef HAVE_CUSTOM_USER_THREADS
+ _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
+#endif
+
+ /* Now start the first CPU. */
+ _pr_primordialCPU = _PR_CreateCPU();
+ _pr_numCPU = 1;
+ _PR_StartCPU(_pr_primordialCPU, me);
+
+ _PR_MD_SET_CURRENT_CPU(_pr_primordialCPU);
+
+ /* Initialize cpu for current thread (could be different from me) */
+ _PR_MD_CURRENT_THREAD()->cpu = _pr_primordialCPU;
+
+ _PR_MD_SET_LAST_THREAD(me);
+
+#else /* Combined MxN model */
+
+ _pr_primordialCPU = _PR_CreateCPU();
+ _pr_numCPU = 1;
+ _PR_CreateThread(PR_SYSTEM_THREAD,
+ _PR_RunCPU,
+ _pr_primordialCPU,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0,
+ _PR_IDLE_THREAD);
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+ _PR_MD_INIT_CPUS();
+}
+
+#ifdef WINNT
+/*
+ * Right now this function merely stops the CPUs and does
+ * not do any other cleanup.
+ *
+ * It is only implemented for WINNT because bug 161998 only
+ * affects the WINNT version of NSPR, but it would be nice
+ * to implement this function for other platforms too.
+ */
+void _PR_CleanupCPUs(void)
+{
+ PRUintn i;
+ PRCList *qp;
+ _PRCPU *cpu;
+
+ _pr_cpus_exit = 1;
+ for (i = 0; i < _pr_numCPU; i++) {
+ _PR_MD_WAKEUP_WAITER(NULL);
+ }
+ for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) {
+ cpu = _PR_CPU_PTR(qp);
+ _PR_MD_JOIN_THREAD(&cpu->thread->md);
+ }
+}
+#endif
+
+static _PRCPUQueue *_PR_CreateCPUQueue(void)
+{
+ PRInt32 index;
+ _PRCPUQueue *cpuQueue;
+ cpuQueue = PR_NEWZAP(_PRCPUQueue);
+
+ _MD_NEW_LOCK( &cpuQueue->runQLock );
+ _MD_NEW_LOCK( &cpuQueue->sleepQLock );
+ _MD_NEW_LOCK( &cpuQueue->miscQLock );
+
+ for (index = 0; index < PR_ARRAY_SIZE(cpuQueue->runQ); index++) {
+ PR_INIT_CLIST( &(cpuQueue->runQ[index]) );
+ }
+ PR_INIT_CLIST( &(cpuQueue->sleepQ) );
+ PR_INIT_CLIST( &(cpuQueue->pauseQ) );
+ PR_INIT_CLIST( &(cpuQueue->suspendQ) );
+ PR_INIT_CLIST( &(cpuQueue->waitingToJoinQ) );
+
+ cpuQueue->numCPUs = 1;
+
+ return cpuQueue;
+}
+
+/*
+ * Create a new CPU.
+ *
+ * This function initializes enough of the _PRCPU structure so
+ * that it can be accessed safely by a global thread or another
+ * CPU. This function does not create the native thread that
+ * will run the CPU nor does it initialize the parts of _PRCPU
+ * that must be initialized by that native thread.
+ *
+ * The reason we cannot simply have the native thread create
+ * and fully initialize a new CPU is that we need to be able to
+ * create a usable _pr_primordialCPU in _PR_InitCPUs without
+ * assuming that the primordial CPU thread we created can run
+ * during NSPR initialization. For example, on Windows while
+ * new threads can be created by DllMain, they won't be able
+ * to run during DLL initialization. If NSPR is initialized
+ * by DllMain, the primordial CPU thread won't run until DLL
+ * initialization is finished.
+ */
+static _PRCPU *_PR_CreateCPU(void)
+{
+ _PRCPU *cpu;
+
+ cpu = PR_NEWZAP(_PRCPU);
+ if (cpu) {
+ cpu->queue = _PR_CreateCPUQueue();
+ if (!cpu->queue) {
+ PR_DELETE(cpu);
+ return NULL;
+ }
+ }
+ return cpu;
+}
+
+/*
+ * Start a new CPU.
+ *
+ * 'cpu' is a _PRCPU structure created by _PR_CreateCPU().
+ * 'thread' is the native thread that will run the CPU.
+ *
+ * If this function fails, 'cpu' is destroyed.
+ */
+static PRStatus _PR_StartCPU(_PRCPU *cpu, PRThread *thread)
+{
+ /*
+ ** Start a new cpu. The assumption this code makes is that the
+ ** underlying operating system creates a stack to go with the new
+ ** native thread. That stack will be used by the cpu when pausing.
+ */
+
+ PR_ASSERT(!_native_threads_only);
+
+ cpu->last_clock = PR_IntervalNow();
+
+ /* Before we create any threads on this CPU we have to
+ * set the current CPU
+ */
+ _PR_MD_SET_CURRENT_CPU(cpu);
+ _PR_MD_INIT_RUNNING_CPU(cpu);
+ thread->cpu = cpu;
+
+ cpu->idle_thread = _PR_CreateThread(PR_SYSTEM_THREAD,
+ _PR_CPU_Idle,
+ (void *)cpu,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0,
+ _PR_IDLE_THREAD);
+
+ if (!cpu->idle_thread) {
+ /* didn't clean up CPU queue XXXMB */
+ PR_DELETE(cpu);
+ return PR_FAILURE;
+ }
+ PR_ASSERT(cpu->idle_thread->cpu == cpu);
+
+ cpu->idle_thread->no_sched = 0;
+
+ cpu->thread = thread;
+
+ if (_pr_cpu_affinity_mask) {
+ PR_SetThreadAffinityMask(thread, _pr_cpu_affinity_mask);
+ }
+
+ /* Created and started a new CPU */
+ _PR_CPU_LIST_LOCK();
+ cpu->id = _pr_cpuID++;
+ PR_APPEND_LINK(&cpu->links, &_PR_CPUQ());
+ _PR_CPU_LIST_UNLOCK();
+
+ return PR_SUCCESS;
+}
+
+#if !defined(_PR_GLOBAL_THREADS_ONLY) && !defined(_PR_LOCAL_THREADS_ONLY)
+/*
+** This code is used during a cpu's initial creation.
+*/
+static void _PR_RunCPU(void *arg)
+{
+ _PRCPU *cpu = (_PRCPU *)arg;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(NULL != me);
+
+ /*
+ * _PR_StartCPU calls _PR_CreateThread to create the
+ * idle thread. Because _PR_CreateThread calls PR_Lock,
+ * the current thread has to remain a global thread
+ * during the _PR_StartCPU call so that it can wait for
+ * the lock if the lock is held by another thread. If
+ * we clear the _PR_GLOBAL_SCOPE flag in
+ * _PR_MD_CREATE_PRIMORDIAL_THREAD, the current thread
+ * will be treated as a local thread and have trouble
+ * waiting for the lock because the CPU is not fully
+ * constructed yet.
+ *
+ * After the CPU is started, it is safe to mark the
+ * current thread as a local thread.
+ */
+
+#ifdef HAVE_CUSTOM_USER_THREADS
+ _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
+#endif
+
+ me->no_sched = 1;
+ _PR_StartCPU(cpu, me);
+
+#ifdef HAVE_CUSTOM_USER_THREADS
+ me->flags &= (~_PR_GLOBAL_SCOPE);
+#endif
+
+ _PR_MD_SET_CURRENT_CPU(cpu);
+ _PR_MD_SET_CURRENT_THREAD(cpu->thread);
+ me->cpu = cpu;
+
+ while(1) {
+ PRInt32 is;
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_MD_START_INTERRUPTS();
+ _PR_MD_SWITCH_CONTEXT(me);
+ }
+}
+#endif
+
+static void PR_CALLBACK _PR_CPU_Idle(void *_cpu)
+{
+ _PRCPU *cpu = (_PRCPU *)_cpu;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(NULL != me);
+
+ me->cpu = cpu;
+ cpu->idle_thread = me;
+ if (_MD_LAST_THREAD()) {
+ _MD_LAST_THREAD()->no_sched = 0;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_MD_SET_INTSOFF(0);
+ }
+ while(1) {
+ PRInt32 is;
+ PRIntervalTime timeout;
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+
+ _PR_RUNQ_LOCK(cpu);
+#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
+#ifdef _PR_HAVE_ATOMIC_OPS
+ _PR_MD_ATOMIC_INCREMENT(&_pr_md_idle_cpus);
+#else
+ _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
+ _pr_md_idle_cpus++;
+ _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
+#endif /* _PR_HAVE_ATOMIC_OPS */
+#endif
+ /* If someone on runq; do a nonblocking PAUSECPU */
+ if (_PR_RUNQREADYMASK(me->cpu) != 0) {
+ _PR_RUNQ_UNLOCK(cpu);
+ timeout = PR_INTERVAL_NO_WAIT;
+ } else {
+ _PR_RUNQ_UNLOCK(cpu);
+
+ _PR_SLEEPQ_LOCK(cpu);
+ if (PR_CLIST_IS_EMPTY(&_PR_SLEEPQ(me->cpu))) {
+ timeout = PR_INTERVAL_NO_TIMEOUT;
+ } else {
+ PRThread *wakeThread;
+ wakeThread = _PR_THREAD_PTR(_PR_SLEEPQ(me->cpu).next);
+ timeout = wakeThread->sleep;
+ }
+ _PR_SLEEPQ_UNLOCK(cpu);
+ }
+
+ /* Wait for an IO to complete */
+ (void)_PR_MD_PAUSE_CPU(timeout);
+
+#ifdef WINNT
+ if (_pr_cpus_exit) {
+ /* _PR_CleanupCPUs tells us to exit */
+ _PR_MD_END_THREAD();
+ }
+#endif
+
+#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
+#ifdef _PR_HAVE_ATOMIC_OPS
+ _PR_MD_ATOMIC_DECREMENT(&_pr_md_idle_cpus);
+#else
+ _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
+ _pr_md_idle_cpus--;
+ _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
+#endif /* _PR_HAVE_ATOMIC_OPS */
+#endif
+
+ _PR_ClockInterrupt();
+
+ /* Now schedule any thread that is on the runq
+ * INTS must be OFF when calling PR_Schedule()
+ */
+ me->state = _PR_RUNNABLE;
+ _PR_MD_SWITCH_CONTEXT(me);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_FAST_INTSON(is);
+ }
+ }
+}
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+
+PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs)
+{
+#if defined(_PR_GLOBAL_THREADS_ONLY) || defined(_PR_LOCAL_THREADS_ONLY)
+
+ /* do nothing */
+
+#else /* combined, MxN thread model */
+
+ PRUintn newCPU;
+ _PRCPU *cpu;
+ PRThread *thr;
+
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (_native_threads_only) {
+ return;
+ }
+
+ _PR_CPU_LIST_LOCK();
+ if (_pr_numCPU < numCPUs) {
+ newCPU = numCPUs - _pr_numCPU;
+ _pr_numCPU = numCPUs;
+ } else {
+ newCPU = 0;
+ }
+ _PR_CPU_LIST_UNLOCK();
+
+ for (; newCPU; newCPU--) {
+ cpu = _PR_CreateCPU();
+ thr = _PR_CreateThread(PR_SYSTEM_THREAD,
+ _PR_RunCPU,
+ cpu,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0,
+ _PR_IDLE_THREAD);
+ }
+#endif
+}
+
+PR_IMPLEMENT(_PRCPU *) _PR_GetPrimordialCPU(void)
+{
+ if (_pr_primordialCPU) {
+ return _pr_primordialCPU;
+ }
+ else {
+ return _PR_MD_CURRENT_CPU();
+ }
+}
diff --git a/nsprpub/pr/src/threads/combined/prucv.c b/nsprpub/pr/src/threads/combined/prucv.c
new file mode 100644
index 0000000000..801d3d2fdd
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/prucv.c
@@ -0,0 +1,679 @@
+/* -*- 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 "prinrval.h"
+#include "prtypes.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
+
+
+/*
+** Notify one thread that it has finished waiting on a condition variable
+** Caller must hold the _PR_CVAR_LOCK(cv)
+*/
+PRBool _PR_NotifyThread (PRThread *thread, PRThread *me)
+{
+ PRBool rv;
+
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+ _PR_THREAD_LOCK(thread);
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+ if ( !_PR_IS_NATIVE_THREAD(thread) ) {
+ if (thread->wait.cvar != NULL) {
+ thread->wait.cvar = NULL;
+
+ _PR_SLEEPQ_LOCK(thread->cpu);
+ /* The notify and timeout can collide; in which case both may
+ * attempt to delete from the sleepQ; only let one do it.
+ */
+ if (thread->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) {
+ _PR_DEL_SLEEPQ(thread, PR_TRUE);
+ }
+ _PR_SLEEPQ_UNLOCK(thread->cpu);
+
+ if (thread->flags & _PR_SUSPENDING) {
+ /*
+ * set thread state to SUSPENDED; a Resume operation
+ * on the thread will move it to the runQ
+ */
+ thread->state = _PR_SUSPENDED;
+ _PR_MISCQ_LOCK(thread->cpu);
+ _PR_ADD_SUSPENDQ(thread, thread->cpu);
+ _PR_MISCQ_UNLOCK(thread->cpu);
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* Make thread runnable */
+ thread->state = _PR_RUNNABLE;
+ _PR_THREAD_UNLOCK(thread);
+
+ _PR_AddThreadToRunQ(me, thread);
+ _PR_MD_WAKEUP_WAITER(thread);
+ }
+
+ rv = PR_TRUE;
+ } else {
+ /* Thread has already been notified */
+ _PR_THREAD_UNLOCK(thread);
+ rv = PR_FALSE;
+ }
+ } else { /* If the thread is a native thread */
+ if (thread->wait.cvar) {
+ thread->wait.cvar = NULL;
+
+ if (thread->flags & _PR_SUSPENDING) {
+ /*
+ * set thread state to SUSPENDED; a Resume operation
+ * on the thread will enable the thread to run
+ */
+ thread->state = _PR_SUSPENDED;
+ } else {
+ thread->state = _PR_RUNNING;
+ }
+ _PR_THREAD_UNLOCK(thread);
+ _PR_MD_WAKEUP_WAITER(thread);
+ rv = PR_TRUE;
+ } else {
+ _PR_THREAD_UNLOCK(thread);
+ rv = PR_FALSE;
+ }
+ }
+
+ return rv;
+}
+
+/*
+ * Notify thread waiting on cvar; called when thread is interrupted
+ * The thread lock is held on entry and released before return
+ */
+void _PR_NotifyLockedThread (PRThread *thread)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRCondVar *cvar;
+ PRThreadPriority pri;
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
+ }
+
+ cvar = thread->wait.cvar;
+ thread->wait.cvar = NULL;
+ _PR_THREAD_UNLOCK(thread);
+
+ _PR_CVAR_LOCK(cvar);
+ _PR_THREAD_LOCK(thread);
+
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_SLEEPQ_LOCK(thread->cpu);
+ /* The notify and timeout can collide; in which case both may
+ * attempt to delete from the sleepQ; only let one do it.
+ */
+ if (thread->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) {
+ _PR_DEL_SLEEPQ(thread, PR_TRUE);
+ }
+ _PR_SLEEPQ_UNLOCK(thread->cpu);
+
+ /* Make thread runnable */
+ pri = thread->priority;
+ thread->state = _PR_RUNNABLE;
+
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+
+ _PR_AddThreadToRunQ(me, thread);
+ _PR_THREAD_UNLOCK(thread);
+
+ _PR_MD_WAKEUP_WAITER(thread);
+ } else {
+ if (thread->flags & _PR_SUSPENDING) {
+ /*
+ * set thread state to SUSPENDED; a Resume operation
+ * on the thread will enable the thread to run
+ */
+ thread->state = _PR_SUSPENDED;
+ } else {
+ thread->state = _PR_RUNNING;
+ }
+ _PR_THREAD_UNLOCK(thread);
+ _PR_MD_WAKEUP_WAITER(thread);
+ }
+
+ _PR_CVAR_UNLOCK(cvar);
+ return;
+}
+
+/*
+** Make the given thread wait for the given condition variable
+*/
+PRStatus _PR_WaitCondVar(
+ PRThread *thread, PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout)
+{
+ PRIntn is;
+ PRStatus rv = PR_SUCCESS;
+
+ PR_ASSERT(thread == _PR_MD_CURRENT_THREAD());
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ if (_PR_PENDING_INTERRUPT(thread)) {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thread->flags &= ~_PR_INTERRUPT;
+ return PR_FAILURE;
+ }
+
+ thread->wait.cvar = cvar;
+ lock->owner = NULL;
+ _PR_MD_WAIT_CV(&cvar->md,&lock->ilock, timeout);
+ thread->wait.cvar = NULL;
+ lock->owner = thread;
+ if (_PR_PENDING_INTERRUPT(thread)) {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thread->flags &= ~_PR_INTERRUPT;
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ if ( !_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_INTSOFF(is);
+ }
+
+ _PR_CVAR_LOCK(cvar);
+ _PR_THREAD_LOCK(thread);
+
+ if (_PR_PENDING_INTERRUPT(thread)) {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thread->flags &= ~_PR_INTERRUPT;
+ _PR_CVAR_UNLOCK(cvar);
+ _PR_THREAD_UNLOCK(thread);
+ if ( !_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_INTSON(is);
+ }
+ return PR_FAILURE;
+ }
+
+ thread->state = _PR_COND_WAIT;
+ thread->wait.cvar = cvar;
+
+ /*
+ ** Put the caller thread on the condition variable's wait Q
+ */
+ PR_APPEND_LINK(&thread->waitQLinks, &cvar->condQ);
+
+ /* Note- for global scope threads, we don't put them on the
+ * global sleepQ, so each global thread must put itself
+ * to sleep only for the time it wants to.
+ */
+ if ( !_PR_IS_NATIVE_THREAD(thread) ) {
+ _PR_SLEEPQ_LOCK(thread->cpu);
+ _PR_ADD_SLEEPQ(thread, timeout);
+ _PR_SLEEPQ_UNLOCK(thread->cpu);
+ }
+ _PR_CVAR_UNLOCK(cvar);
+ _PR_THREAD_UNLOCK(thread);
+
+ /*
+ ** Release lock protecting the condition variable and thereby giving time
+ ** to the next thread which can potentially notify on the condition variable
+ */
+ PR_Unlock(lock);
+
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN,
+ ("PR_Wait: cvar=%p waiting for %d", cvar, timeout));
+
+ rv = _PR_MD_WAIT(thread, timeout);
+
+ _PR_CVAR_LOCK(cvar);
+ PR_REMOVE_LINK(&thread->waitQLinks);
+ _PR_CVAR_UNLOCK(cvar);
+
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN,
+ ("PR_Wait: cvar=%p done waiting", cvar));
+
+ if ( !_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_INTSON(is);
+ }
+
+ /* Acquire lock again that we had just relinquished */
+ PR_Lock(lock);
+
+ if (_PR_PENDING_INTERRUPT(thread)) {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thread->flags &= ~_PR_INTERRUPT;
+ return PR_FAILURE;
+ }
+
+ return rv;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+void _PR_NotifyCondVar(PRCondVar *cvar, PRThread *me)
+{
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ _PR_MD_NOTIFY_CV(&cvar->md, &cvar->lock->ilock);
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ PRCList *q;
+ PRIntn is;
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+ _PR_CVAR_LOCK(cvar);
+ q = cvar->condQ.next;
+ while (q != &cvar->condQ) {
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("_PR_NotifyCondVar: cvar=%p", cvar));
+ if (_PR_THREAD_CONDQ_PTR(q)->wait.cvar) {
+ if (_PR_NotifyThread(_PR_THREAD_CONDQ_PTR(q), me) == PR_TRUE) {
+ break;
+ }
+ }
+ q = q->next;
+ }
+ _PR_CVAR_UNLOCK(cvar);
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/*
+** Cndition variable debugging log info.
+*/
+PRUint32 _PR_CondVarToString(PRCondVar *cvar, char *buf, PRUint32 buflen)
+{
+ PRUint32 nb;
+
+ if (cvar->lock->owner) {
+ nb = PR_snprintf(buf, buflen, "[%p] owner=%ld[%p]",
+ cvar, cvar->lock->owner->id, cvar->lock->owner);
+ } else {
+ nb = PR_snprintf(buf, buflen, "[%p]", cvar);
+ }
+ return nb;
+}
+
+/*
+** Expire condition variable waits that are ready to expire. "now" is the current
+** time.
+*/
+void _PR_ClockInterrupt(void)
+{
+ PRThread *thread, *me = _PR_MD_CURRENT_THREAD();
+ _PRCPU *cpu = me->cpu;
+ PRIntervalTime elapsed, now;
+
+ PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
+ /* Figure out how much time elapsed since the last clock tick */
+ now = PR_IntervalNow();
+ elapsed = now - cpu->last_clock;
+ cpu->last_clock = now;
+
+ PR_LOG(_pr_clock_lm, PR_LOG_MAX,
+ ("ExpireWaits: elapsed=%lld usec", elapsed));
+
+ while(1) {
+ _PR_SLEEPQ_LOCK(cpu);
+ if (_PR_SLEEPQ(cpu).next == &_PR_SLEEPQ(cpu)) {
+ _PR_SLEEPQ_UNLOCK(cpu);
+ break;
+ }
+
+ thread = _PR_THREAD_PTR(_PR_SLEEPQ(cpu).next);
+ PR_ASSERT(thread->cpu == cpu);
+
+ if (elapsed < thread->sleep) {
+ thread->sleep -= elapsed;
+ _PR_SLEEPQMAX(thread->cpu) -= elapsed;
+ _PR_SLEEPQ_UNLOCK(cpu);
+ break;
+ }
+ _PR_SLEEPQ_UNLOCK(cpu);
+
+ PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));
+
+ _PR_THREAD_LOCK(thread);
+
+ if (thread->cpu != cpu) {
+ /*
+ ** The thread was switched to another CPU
+ ** between the time we unlocked the sleep
+ ** queue and the time we acquired the thread
+ ** lock, so it is none of our business now.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ continue;
+ }
+
+ /*
+ ** Consume this sleeper's amount of elapsed time from the elapsed
+ ** time value. The next remaining piece of elapsed time will be
+ ** available for the next sleeping thread's timer.
+ */
+ _PR_SLEEPQ_LOCK(cpu);
+ PR_ASSERT(!(thread->flags & _PR_ON_PAUSEQ));
+ if (thread->flags & _PR_ON_SLEEPQ) {
+ _PR_DEL_SLEEPQ(thread, PR_FALSE);
+ elapsed -= thread->sleep;
+ _PR_SLEEPQ_UNLOCK(cpu);
+ } else {
+ /* Thread was already handled; Go get another one */
+ _PR_SLEEPQ_UNLOCK(cpu);
+ _PR_THREAD_UNLOCK(thread);
+ continue;
+ }
+
+ /* Notify the thread waiting on the condition variable */
+ if (thread->flags & _PR_SUSPENDING) {
+ PR_ASSERT((thread->state == _PR_IO_WAIT) ||
+ (thread->state == _PR_COND_WAIT));
+ /*
+ ** Thread is suspended and its condition timeout
+ ** expired. Transfer thread from sleepQ to suspendQ.
+ */
+ thread->wait.cvar = NULL;
+ _PR_MISCQ_LOCK(cpu);
+ thread->state = _PR_SUSPENDED;
+ _PR_ADD_SUSPENDQ(thread, cpu);
+ _PR_MISCQ_UNLOCK(cpu);
+ } else {
+ if (thread->wait.cvar) {
+ PRThreadPriority pri;
+
+ /* Do work very similar to what _PR_NotifyThread does */
+ PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
+
+ /* Make thread runnable */
+ pri = thread->priority;
+ thread->state = _PR_RUNNABLE;
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+
+ PR_ASSERT(thread->cpu == cpu);
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thread, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+
+ if (pri > me->priority) {
+ _PR_SET_RESCHED_FLAG();
+ }
+
+ thread->wait.cvar = NULL;
+
+ _PR_MD_WAKEUP_WAITER(thread);
+
+ } else if (thread->io_pending == PR_TRUE) {
+ /* Need to put IO sleeper back on runq */
+ int pri = thread->priority;
+
+ thread->io_suspended = PR_TRUE;
+#ifdef WINNT
+ /*
+ * For NT, record the cpu on which I/O was issued
+ * I/O cancellation is done on the same cpu
+ */
+ thread->md.thr_bound_cpu = cpu;
+#endif
+
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+ PR_ASSERT(thread->cpu == cpu);
+ thread->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thread, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ }
+ }
+ _PR_THREAD_UNLOCK(thread);
+ }
+}
+
+/************************************************************************/
+
+/*
+** Create a new condition variable.
+** "lock" is the lock to use with the condition variable.
+**
+** Condition variables are synchronization objects that threads can use
+** to wait for some condition to occur.
+**
+** This may fail if memory is tight or if some operating system resource
+** is low.
+*/
+PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock)
+{
+ PRCondVar *cvar;
+
+ cvar = PR_NEWZAP(PRCondVar);
+ if (cvar) {
+ if (_PR_InitCondVar(cvar, lock) != PR_SUCCESS) {
+ PR_DELETE(cvar);
+ return NULL;
+ }
+ } else {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ return cvar;
+}
+
+PRStatus _PR_InitCondVar(PRCondVar *cvar, PRLock *lock)
+{
+ PR_ASSERT(lock != NULL);
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ if(_PR_MD_NEW_CV(&cvar->md)) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ return PR_FAILURE;
+ }
+#endif
+ if (_PR_MD_NEW_LOCK(&(cvar->ilock)) != PR_SUCCESS) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ return PR_FAILURE;
+ }
+ cvar->lock = lock;
+ PR_INIT_CLIST(&cvar->condQ);
+ return PR_SUCCESS;
+}
+
+/*
+** Destroy a condition variable. There must be no thread
+** waiting on the condvar. The caller is responsible for guaranteeing
+** that the condvar is no longer in use.
+**
+*/
+PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar)
+{
+ _PR_FreeCondVar(cvar);
+ PR_DELETE(cvar);
+}
+
+void _PR_FreeCondVar(PRCondVar *cvar)
+{
+ PR_ASSERT(cvar->condQ.next == &cvar->condQ);
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ _PR_MD_FREE_CV(&cvar->md);
+#endif
+ _PR_MD_FREE_LOCK(&(cvar->ilock));
+}
+
+/*
+** Wait for a notify on the condition variable. Sleep for "tiemout" amount
+** of ticks (if "timeout" is zero then the sleep is indefinite). While
+** the thread is waiting it unlocks lock. When the wait has
+** finished the thread regains control of the condition variable after
+** locking the associated lock.
+**
+** The thread waiting on the condvar will be resumed when the condvar is
+** notified (assuming the thread is the next in line to receive the
+** notify) or when the timeout elapses.
+**
+** Returns PR_FAILURE if the caller has not locked the lock associated
+** with the condition variable or the thread has been interrupted.
+*/
+extern PRThread *suspendAllThread;
+PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(cvar->lock->owner == me);
+ PR_ASSERT(me != suspendAllThread);
+ if (cvar->lock->owner != me) {
+ return PR_FAILURE;
+ }
+
+ return _PR_WaitCondVar(me, cvar, cvar->lock, timeout);
+}
+
+/*
+** Notify the highest priority thread waiting on the condition
+** variable. If a thread is waiting on the condition variable (using
+** PR_Wait) then it is awakened and begins waiting on the lock.
+*/
+PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(cvar->lock->owner == me);
+ PR_ASSERT(me != suspendAllThread);
+ if (cvar->lock->owner != me) {
+ return PR_FAILURE;
+ }
+
+ _PR_NotifyCondVar(cvar, me);
+ return PR_SUCCESS;
+}
+
+/*
+** Notify all of the threads waiting on the condition variable. All of
+** threads are notified in turn. The highest priority thread will
+** probably acquire the lock.
+*/
+PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar)
+{
+ PRCList *q;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(cvar->lock->owner == me);
+ if (cvar->lock->owner != me) {
+ return PR_FAILURE;
+ }
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ _PR_MD_NOTIFYALL_CV(&cvar->md, &cvar->lock->ilock);
+ return PR_SUCCESS;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_CVAR_LOCK(cvar);
+ q = cvar->condQ.next;
+ while (q != &cvar->condQ) {
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_NotifyAll: cvar=%p", cvar));
+ _PR_NotifyThread(_PR_THREAD_CONDQ_PTR(q), me);
+ q = q->next;
+ }
+ _PR_CVAR_UNLOCK(cvar);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+
+ return PR_SUCCESS;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+
+/*********************************************************************/
+/*********************************************************************/
+/********************ROUTINES FOR DCE EMULATION***********************/
+/*********************************************************************/
+/*********************************************************************/
+#include "prpdce.h"
+
+PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void)
+{
+ PRCondVar *cvar = PR_NEWZAP(PRCondVar);
+ if (NULL != cvar)
+ {
+ if (_PR_MD_NEW_LOCK(&(cvar->ilock)) == PR_FAILURE)
+ {
+ PR_DELETE(cvar); cvar = NULL;
+ }
+ else
+ {
+ PR_INIT_CLIST(&cvar->condQ);
+ cvar->lock = _PR_NAKED_CV_LOCK;
+ }
+
+ }
+ return cvar;
+}
+
+PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar *cvar)
+{
+ PR_ASSERT(cvar->condQ.next == &cvar->condQ);
+ PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock);
+
+ _PR_MD_FREE_LOCK(&(cvar->ilock));
+
+ PR_DELETE(cvar);
+}
+
+PR_IMPLEMENT(PRStatus) PRP_NakedWait(
+ PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock);
+ return _PR_WaitCondVar(me, cvar, lock, timeout);
+} /* PRP_NakedWait */
+
+PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock);
+
+ _PR_NotifyCondVar(cvar, me);
+
+ return PR_SUCCESS;
+} /* PRP_NakedNotify */
+
+PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar)
+{
+ PRCList *q;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock);
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_MD_LOCK( &(cvar->ilock) );
+ q = cvar->condQ.next;
+ while (q != &cvar->condQ) {
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_NotifyAll: cvar=%p", cvar));
+ _PR_NotifyThread(_PR_THREAD_CONDQ_PTR(q), me);
+ q = q->next;
+ }
+ _PR_MD_UNLOCK( &(cvar->ilock) );
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+
+ return PR_SUCCESS;
+} /* PRP_NakedBroadcast */
+
diff --git a/nsprpub/pr/src/threads/combined/prulock.c b/nsprpub/pr/src/threads/combined/prulock.c
new file mode 100644
index 0000000000..69c27d149f
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/prulock.c
@@ -0,0 +1,457 @@
+/* -*- 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"
+
+#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
+
+
+void _PR_InitLocks(void)
+{
+ _PR_MD_INIT_LOCKS();
+}
+
+/*
+** Deal with delayed interrupts/requested reschedule during interrupt
+** re-enables.
+*/
+void _PR_IntsOn(_PRCPU *cpu)
+{
+ PRUintn missed, pri, i;
+ _PRInterruptTable *it;
+ PRThread *me;
+
+ PR_ASSERT(cpu); /* Global threads don't have CPUs */
+ PR_ASSERT(_PR_MD_GET_INTSOFF() > 0);
+ me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+
+ /*
+ ** Process delayed interrupts. This logic is kinda scary because we
+ ** need to avoid losing an interrupt (it's ok to delay an interrupt
+ ** until later).
+ **
+ ** There are two missed state words. _pr_ints.where indicates to the
+ ** interrupt handler which state word is currently safe for
+ ** modification.
+ **
+ ** This code scans both interrupt state words, using the where flag
+ ** to indicate to the interrupt which state word is safe for writing.
+ ** If an interrupt comes in during a scan the other word will be
+ ** modified. This modification will be noticed during the next
+ ** iteration of the loop or during the next call to this routine.
+ */
+ for (i = 0; i < 2; i++) {
+ cpu->where = (1 - i);
+ missed = cpu->u.missed[i];
+ if (missed != 0) {
+ cpu->u.missed[i] = 0;
+ for (it = _pr_interruptTable; it->name; it++) {
+ if (missed & it->missed_bit) {
+ PR_LOG(_pr_sched_lm, PR_LOG_MIN,
+ ("IntsOn[0]: %s intr", it->name));
+ (*it->handler)();
+ }
+ }
+ }
+ }
+
+ if (cpu->u.missed[3] != 0) {
+ _PRCPU *cpu;
+
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_RUNNABLE;
+ pri = me->priority;
+
+ cpu = me->cpu;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(me, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_SWITCH_CONTEXT(me);
+ }
+}
+
+/*
+** Unblock the first runnable waiting thread. Skip over
+** threads that are trying to be suspended
+** Note: Caller must hold _PR_LOCK_LOCK()
+*/
+void _PR_UnblockLockWaiter(PRLock *lock)
+{
+ PRThread *t = NULL;
+ PRThread *me;
+ PRCList *q;
+
+ q = lock->waitQ.next;
+ PR_ASSERT(q != &lock->waitQ);
+ while (q != &lock->waitQ) {
+ /* Unblock first waiter */
+ t = _PR_THREAD_CONDQ_PTR(q);
+
+ /*
+ ** We are about to change the thread's state to runnable and for local
+ ** threads, we are going to assign a cpu to it. So, protect thread's
+ ** data structure.
+ */
+ _PR_THREAD_LOCK(t);
+
+ if (t->flags & _PR_SUSPENDING) {
+ q = q->next;
+ _PR_THREAD_UNLOCK(t);
+ continue;
+ }
+
+ /* Found a runnable thread */
+ PR_ASSERT(t->state == _PR_LOCK_WAIT);
+ PR_ASSERT(t->wait.lock == lock);
+ t->wait.lock = 0;
+ PR_REMOVE_LINK(&t->waitQLinks); /* take it off lock's waitQ */
+
+ /*
+ ** If this is a native thread, nothing else to do except to wake it
+ ** up by calling the machine dependent wakeup routine.
+ **
+ ** If this is a local thread, we need to assign it a cpu and
+ ** put the thread on that cpu's run queue. There are two cases to
+ ** take care of. If the currently running thread is also a local
+ ** thread, we just assign our own cpu to that thread and put it on
+ ** the cpu's run queue. If the the currently running thread is a
+ ** native thread, we assign the primordial cpu to it (on NT,
+ ** MD_WAKEUP handles the cpu assignment).
+ */
+
+ if ( !_PR_IS_NATIVE_THREAD(t) ) {
+
+ t->state = _PR_RUNNABLE;
+
+ me = _PR_MD_CURRENT_THREAD();
+
+ _PR_AddThreadToRunQ(me, t);
+ _PR_THREAD_UNLOCK(t);
+ } else {
+ t->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(t);
+ }
+ _PR_MD_WAKEUP_WAITER(t);
+ break;
+ }
+ return;
+}
+
+/************************************************************************/
+
+
+PR_IMPLEMENT(PRLock*) PR_NewLock(void)
+{
+ PRLock *lock;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ lock = PR_NEWZAP(PRLock);
+ if (lock) {
+ if (_PR_InitLock(lock) != PR_SUCCESS) {
+ PR_DELETE(lock);
+ return NULL;
+ }
+ }
+ return lock;
+}
+
+PRStatus _PR_InitLock(PRLock *lock)
+{
+ if (_PR_MD_NEW_LOCK(&lock->ilock) != PR_SUCCESS) {
+ return PR_FAILURE;
+ }
+ PR_INIT_CLIST(&lock->links);
+ PR_INIT_CLIST(&lock->waitQ);
+ return PR_SUCCESS;
+}
+
+/*
+** Destroy the given lock "lock". There is no point in making this race
+** free because if some other thread has the pointer to this lock all
+** bets are off.
+*/
+PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock)
+{
+ _PR_FreeLock(lock);
+ PR_DELETE(lock);
+}
+
+void _PR_FreeLock(PRLock *lock)
+{
+ PR_ASSERT(lock->owner == 0);
+ _PR_MD_FREE_LOCK(&lock->ilock);
+}
+
+extern PRThread *suspendAllThread;
+/*
+** Lock the lock.
+*/
+PR_IMPLEMENT(void) PR_Lock(PRLock *lock)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntn is;
+ PRThread *t;
+ PRCList *q;
+
+ PR_ASSERT(me != suspendAllThread);
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+ PR_ASSERT(lock != NULL);
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ _PR_MD_LOCK(&lock->ilock);
+ PR_ASSERT(lock->owner == 0);
+ lock->owner = me;
+ return;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ if (_native_threads_only) {
+ _PR_MD_LOCK(&lock->ilock);
+ PR_ASSERT(lock->owner == 0);
+ lock->owner = me;
+ return;
+ }
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+retry:
+ _PR_LOCK_LOCK(lock);
+ if (lock->owner == 0) {
+ /* Just got the lock */
+ lock->owner = me;
+ lock->priority = me->priority;
+ /* Add the granted lock to this owning thread's lock list */
+ PR_APPEND_LINK(&lock->links, &me->lockList);
+ _PR_LOCK_UNLOCK(lock);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_FAST_INTSON(is);
+ }
+ return;
+ }
+
+ /* If this thread already owns this lock, then it is a deadlock */
+ PR_ASSERT(lock->owner != me);
+
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+#if 0
+ if (me->priority > lock->owner->priority) {
+ /*
+ ** Give the lock owner a priority boost until we get the
+ ** lock. Record the priority we boosted it to.
+ */
+ lock->boostPriority = me->priority;
+ _PR_SetThreadPriority(lock->owner, me->priority);
+ }
+#endif
+
+ /*
+ Add this thread to the asked for lock's list of waiting threads. We
+ add this thread thread in the right priority order so when the unlock
+ occurs, the thread with the higher priority will get the lock.
+ */
+ q = lock->waitQ.next;
+ if (q == &lock->waitQ || _PR_THREAD_CONDQ_PTR(q)->priority ==
+ _PR_THREAD_CONDQ_PTR(lock->waitQ.prev)->priority) {
+ /*
+ * If all the threads in the lock waitQ have the same priority,
+ * then avoid scanning the list: insert the element at the end.
+ */
+ q = &lock->waitQ;
+ } else {
+ /* Sort thread into lock's waitQ at appropriate point */
+ /* Now scan the list for where to insert this entry */
+ while (q != &lock->waitQ) {
+ t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next);
+ if (me->priority > t->priority) {
+ /* Found a lower priority thread to insert in front of */
+ break;
+ }
+ q = q->next;
+ }
+ }
+ PR_INSERT_BEFORE(&me->waitQLinks, q);
+
+ /*
+ Now grab the threadLock since we are about to change the state. We have
+ to do this since a PR_Suspend or PR_SetThreadPriority type call that takes
+ a PRThread* as an argument could be changing the state of this thread from
+ a thread running on a different cpu.
+ */
+
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_LOCK_WAIT;
+ me->wait.lock = lock;
+ _PR_THREAD_UNLOCK(me);
+
+ _PR_LOCK_UNLOCK(lock);
+
+ _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
+ goto retry;
+
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/*
+** Unlock the lock.
+*/
+PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock)
+{
+ PRCList *q;
+ PRThreadPriority pri, boost;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(lock != NULL);
+ PR_ASSERT(lock->owner == me);
+ PR_ASSERT(me != suspendAllThread);
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+ if (lock->owner != me) {
+ return PR_FAILURE;
+ }
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ lock->owner = 0;
+ _PR_MD_UNLOCK(&lock->ilock);
+ return PR_SUCCESS;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ if (_native_threads_only) {
+ lock->owner = 0;
+ _PR_MD_UNLOCK(&lock->ilock);
+ return PR_SUCCESS;
+ }
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_LOCK_LOCK(lock);
+
+ /* Remove the lock from the owning thread's lock list */
+ PR_REMOVE_LINK(&lock->links);
+ pri = lock->priority;
+ boost = lock->boostPriority;
+ if (boost > pri) {
+ /*
+ ** We received a priority boost during the time we held the lock.
+ ** We need to figure out what priority to move to by scanning
+ ** down our list of lock's that we are still holding and using
+ ** the highest boosted priority found.
+ */
+ q = me->lockList.next;
+ while (q != &me->lockList) {
+ PRLock *ll = _PR_LOCK_PTR(q);
+ if (ll->boostPriority > pri) {
+ pri = ll->boostPriority;
+ }
+ q = q->next;
+ }
+ if (pri != me->priority) {
+ _PR_SetThreadPriority(me, pri);
+ }
+ }
+
+ /* Unblock the first waiting thread */
+ q = lock->waitQ.next;
+ if (q != &lock->waitQ) {
+ _PR_UnblockLockWaiter(lock);
+ }
+ lock->boostPriority = PR_PRIORITY_LOW;
+ lock->owner = 0;
+ _PR_LOCK_UNLOCK(lock);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+ return PR_SUCCESS;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/*
+** If the current thread owns |lock|, this assertion is guaranteed to
+** succeed. Otherwise, the behavior of this function is undefined.
+*/
+PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock *lock)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(lock->owner == me);
+}
+
+/*
+** Test and then lock the lock if it's not already locked by some other
+** thread. Return PR_FALSE if some other thread owned the lock at the
+** time of the call.
+*/
+PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRBool rv = PR_FALSE;
+ PRIntn is;
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ is = _PR_MD_TEST_AND_LOCK(&lock->ilock);
+ if (is == 0) {
+ lock->owner = me;
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+#ifndef _PR_LOCAL_THREADS_ONLY
+ if (_native_threads_only) {
+ is = _PR_MD_TEST_AND_LOCK(&lock->ilock);
+ if (is == 0) {
+ lock->owner = me;
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+ }
+#endif
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+
+ _PR_LOCK_LOCK(lock);
+ if (lock->owner == 0) {
+ /* Just got the lock */
+ lock->owner = me;
+ lock->priority = me->priority;
+ /* Add the granted lock to this owning thread's lock list */
+ PR_APPEND_LINK(&lock->links, &me->lockList);
+ rv = PR_TRUE;
+ }
+ _PR_LOCK_UNLOCK(lock);
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+ return rv;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/************************************************************************/
+/************************************************************************/
+/***********************ROUTINES FOR DCE EMULATION***********************/
+/************************************************************************/
+/************************************************************************/
+PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock)
+{
+ return (PR_TestAndLock(lock)) ? PR_SUCCESS : PR_FAILURE;
+}
diff --git a/nsprpub/pr/src/threads/combined/prustack.c b/nsprpub/pr/src/threads/combined/prustack.c
new file mode 100644
index 0000000000..3f5452c48c
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/prustack.c
@@ -0,0 +1,175 @@
+/* -*- 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"
+
+/* List of free stack virtual memory chunks */
+PRLock *_pr_stackLock;
+PRCList _pr_freeStacks = PR_INIT_STATIC_CLIST(&_pr_freeStacks);
+PRIntn _pr_numFreeStacks;
+PRIntn _pr_maxFreeStacks = 4;
+
+#ifdef DEBUG
+/*
+** A variable that can be set via the debugger...
+*/
+PRBool _pr_debugStacks = PR_FALSE;
+#endif
+
+/* How much space to leave between the stacks, at each end */
+#define REDZONE (2 << _pr_pageShift)
+
+#define _PR_THREAD_STACK_PTR(_qp) \
+ ((PRThreadStack*) ((char*) (_qp) - offsetof(PRThreadStack,links)))
+
+void _PR_InitStacks(void)
+{
+ _pr_stackLock = PR_NewLock();
+}
+
+void _PR_CleanupStacks(void)
+{
+ if (_pr_stackLock) {
+ PR_DestroyLock(_pr_stackLock);
+ _pr_stackLock = NULL;
+ }
+}
+
+/*
+** Allocate a stack for a thread.
+*/
+PRThreadStack *_PR_NewStack(PRUint32 stackSize)
+{
+ PRCList *qp;
+ PRThreadStack *ts;
+ PRThread *thr;
+
+ /*
+ ** Trim the list of free stacks. Trim it backwards, tossing out the
+ ** oldest stack found first (this way more recent stacks have a
+ ** chance of being present in the data cache).
+ */
+ PR_Lock(_pr_stackLock);
+ qp = _pr_freeStacks.prev;
+ while ((_pr_numFreeStacks > _pr_maxFreeStacks) && (qp != &_pr_freeStacks)) {
+ ts = _PR_THREAD_STACK_PTR(qp);
+ thr = _PR_THREAD_STACK_TO_PTR(ts);
+ qp = qp->prev;
+ /*
+ * skip stacks which are still being used
+ */
+ if (thr->no_sched) {
+ continue;
+ }
+ PR_REMOVE_LINK(&ts->links);
+
+ /* Give platform OS to clear out the stack for debugging */
+ _PR_MD_CLEAR_STACK(ts);
+
+ _pr_numFreeStacks--;
+ _PR_DestroySegment(ts->seg);
+ PR_DELETE(ts);
+ }
+
+ /*
+ ** Find a free thread stack. This searches the list of free'd up
+ ** virtually mapped thread stacks.
+ */
+ qp = _pr_freeStacks.next;
+ ts = 0;
+ while (qp != &_pr_freeStacks) {
+ ts = _PR_THREAD_STACK_PTR(qp);
+ thr = _PR_THREAD_STACK_TO_PTR(ts);
+ qp = qp->next;
+ /*
+ * skip stacks which are still being used
+ */
+ if ((!(thr->no_sched)) && ((ts->allocSize - 2*REDZONE) >= stackSize)) {
+ /*
+ ** Found a stack that is not in use and is big enough. Change
+ ** stackSize to fit it.
+ */
+ stackSize = ts->allocSize - 2*REDZONE;
+ PR_REMOVE_LINK(&ts->links);
+ _pr_numFreeStacks--;
+ ts->links.next = 0;
+ ts->links.prev = 0;
+ PR_Unlock(_pr_stackLock);
+ goto done;
+ }
+ ts = 0;
+ }
+ PR_Unlock(_pr_stackLock);
+
+ if (!ts) {
+ /* Make a new thread stack object. */
+ ts = PR_NEWZAP(PRThreadStack);
+ if (!ts) {
+ return NULL;
+ }
+
+ /*
+ ** Assign some of the virtual space to the new stack object. We
+ ** may not get that piece of VM, but if nothing else we will
+ ** advance the pointer so we don't collide (unless the OS screws
+ ** up).
+ */
+ ts->allocSize = stackSize + 2*REDZONE;
+ ts->seg = _PR_NewSegment(ts->allocSize, 0);
+ if (!ts->seg) {
+ PR_DELETE(ts);
+ return NULL;
+ }
+ }
+
+done:
+ ts->allocBase = (char*)ts->seg->vaddr;
+ ts->flags = _PR_STACK_MAPPED;
+ ts->stackSize = stackSize;
+
+#ifdef HAVE_STACK_GROWING_UP
+ ts->stackTop = ts->allocBase + REDZONE;
+ ts->stackBottom = ts->stackTop + stackSize;
+#else
+ ts->stackBottom = ts->allocBase + REDZONE;
+ ts->stackTop = ts->stackBottom + stackSize;
+#endif
+
+ PR_LOG(_pr_thread_lm, PR_LOG_NOTICE,
+ ("thread stack: base=0x%x limit=0x%x bottom=0x%x top=0x%x\n",
+ ts->allocBase, ts->allocBase + ts->allocSize - 1,
+ ts->allocBase + REDZONE,
+ ts->allocBase + REDZONE + stackSize - 1));
+
+ _PR_MD_INIT_STACK(ts,REDZONE);
+
+ return ts;
+}
+
+/*
+** Free the stack for the current thread
+*/
+void _PR_FreeStack(PRThreadStack *ts)
+{
+ if (!ts) {
+ return;
+ }
+ if (ts->flags & _PR_STACK_PRIMORDIAL) {
+ PR_DELETE(ts);
+ return;
+ }
+
+ /*
+ ** Put the stack on the free list. This is done because we are still
+ ** using the stack. Next time a thread is created we will trim the
+ ** list down; it's safe to do it then because we will have had to
+ ** context switch to a live stack before another thread can be
+ ** created.
+ */
+ PR_Lock(_pr_stackLock);
+ PR_APPEND_LINK(&ts->links, _pr_freeStacks.prev);
+ _pr_numFreeStacks++;
+ PR_Unlock(_pr_stackLock);
+}
diff --git a/nsprpub/pr/src/threads/combined/pruthr.c b/nsprpub/pr/src/threads/combined/pruthr.c
new file mode 100644
index 0000000000..44a0820072
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/pruthr.c
@@ -0,0 +1,1942 @@
+/* -*- 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*) ((((PRWord)&type) >> _pr_pageShift)
+ << _pr_pageShift);
+#else
+#if defined(SOLARIS) || defined (UNIXWARE) && defined (USR_SVR4_THREADS)
+ stack->stackTop = (char*) &thread;
+#else
+ stack->stackTop = (char*) ((((PRWord)&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*) ((((PRWord)&ts) >> _pr_pageShift)
+ << _pr_pageShift);
+ ts->stackBottom = ts->allocBase + ts->stackSize;
+ ts->stackTop = ts->allocBase;
+#else
+ ts->allocBase = (char*) ((((PRWord)&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;
+ 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 Solaris, and Windows, foreign threads are detached when
+ * they terminate.
+ */
+#if !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
+}