diff nspr/pr/src/pthreads/ptthread.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nspr/pr/src/pthreads/ptthread.c	Mon Jul 28 10:47:06 2014 +0200
@@ -0,0 +1,1814 @@
+/* -*- 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/. */
+
+/*
+** File:            ptthread.c
+** Descritpion:        Implemenation for threds using pthreds
+** Exports:            ptthread.h
+*/
+
+#if defined(_PR_PTHREADS) || defined(_PR_DCETHREADS)
+
+#include "prlog.h"
+#include "primpl.h"
+#include "prpdce.h"
+
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <dlfcn.h>
+
+#ifdef SYMBIAN
+/* In Open C sched_get_priority_min/max do not work properly, so we undefine
+ * _POSIX_THREAD_PRIORITY_SCHEDULING here.
+ */
+#undef _POSIX_THREAD_PRIORITY_SCHEDULING
+#endif
+
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+#undef _POSIX_THREAD_PRIORITY_SCHEDULING
+#include <sys/resource.h>
+#ifndef HAVE_GETTID
+#define gettid() (syscall(SYS_gettid))
+#endif
+#endif
+
+/*
+ * Record whether or not we have the privilege to set the scheduling
+ * policy and priority of threads.  0 means that privilege is available.
+ * EPERM means that privilege is not available.
+ */
+
+static PRIntn pt_schedpriv = 0;
+extern PRLock *_pr_sleeplock;
+
+static struct _PT_Bookeeping
+{
+    PRLock *ml;                 /* a lock to protect ourselves */
+    PRCondVar *cv;              /* used to signal global things */
+    PRInt32 system, user;       /* a count of the two different types */
+    PRUintn this_many;          /* number of threads allowed for exit */
+    pthread_key_t key;          /* thread private data key */
+    PRBool keyCreated;          /* whether 'key' should be deleted */
+    PRThread *first, *last;     /* list of threads we know about */
+#if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+    PRInt32 minPrio, maxPrio;   /* range of scheduling priorities */
+#endif
+} pt_book = {0};
+
+static void _pt_thread_death(void *arg);
+static void _pt_thread_death_internal(void *arg, PRBool callDestructors);
+static void init_pthread_gc_support(void);
+
+#if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+static PRIntn pt_PriorityMap(PRThreadPriority pri)
+{
+#ifdef NTO
+    /* This priority algorithm causes lots of problems on Neutrino
+     * for now I have just hard coded everything to run at priority 10
+     * until I can come up with a new algorithm.
+     *     Jerry.Kirk@Nexwarecorp.com
+     */
+    return 10;
+#else
+    return pt_book.minPrio +
+	    pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST;
+#endif
+}
+#elif defined(_PR_NICE_PRIORITY_SCHEDULING)
+/*
+ * This functions maps higher priorities to lower nice values relative to the
+ * nice value specified in the |nice| parameter. The corresponding relative
+ * adjustments are:
+ *
+ * PR_PRIORITY_LOW    +1
+ * PR_PRIORITY_NORMAL  0
+ * PR_PRIORITY_HIGH   -1
+ * PR_PRIORITY_URGENT -2
+ */
+static int pt_RelativePriority(int nice, PRThreadPriority pri)
+{
+    return nice + (1 - pri);
+}
+#endif
+
+/*
+** Initialize a stack for a native pthread thread
+*/
+static void _PR_InitializeStack(PRThreadStack *ts)
+{
+    if( ts && (ts->stackTop == 0) ) {
+        ts->allocBase = (char *) &ts;
+        ts->allocSize = ts->stackSize;
+
+        /*
+        ** Setup stackTop and stackBottom values.
+        */
+#ifdef HAVE_STACK_GROWING_UP
+        ts->stackBottom = ts->allocBase + ts->stackSize;
+        ts->stackTop = ts->allocBase;
+#else
+        ts->stackTop    = ts->allocBase;
+        ts->stackBottom = ts->allocBase - ts->stackSize;
+#endif
+    }
+}
+
+static void *_pt_root(void *arg)
+{
+    PRIntn rv;
+    PRThread *thred = (PRThread*)arg;
+    PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE;
+    pthread_t id = pthread_self();
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+    pid_t tid;
+#endif
+
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+    /*
+     * We need to know the kernel thread ID of each thread in order to
+     * set its nice value hence we do it here instead of at creation time.
+     */
+    tid = gettid();
+    errno = 0;
+    rv = getpriority(PRIO_PROCESS, 0);
+
+    /* If we cannot read the main thread's nice value don't try to change the
+     * new thread's nice value. */
+    if (errno == 0) {
+        setpriority(PRIO_PROCESS, tid,
+                    pt_RelativePriority(rv, thred->priority));
+    }
+#endif
+
+    /*
+    ** DCE Threads can't detach during creation, so do it late.
+    ** I would like to do it only here, but that doesn't seem
+    ** to work.
+    */
+#if defined(_PR_DCETHREADS)
+    if (detached)
+    {
+        /* pthread_detach() modifies its argument, so we must pass a copy */
+        pthread_t self = id;
+        rv = pthread_detach(&self);
+        PR_ASSERT(0 == rv);
+    }
+#endif /* defined(_PR_DCETHREADS) */
+
+    /* Set up the thread stack information */
+    _PR_InitializeStack(thred->stack);
+
+    /*
+     * Set within the current thread the pointer to our object.
+     * This object will be deleted when the thread termintates,
+     * whether in a join or detached (see _PR_InitThreads()).
+     */
+    rv = pthread_setspecific(pt_book.key, thred);
+    PR_ASSERT(0 == rv);
+
+    /* make the thread visible to the rest of the runtime */
+    PR_Lock(pt_book.ml);
+    /*
+     * Both the parent thread and this new thread set thred->id.
+     * The new thread must ensure that thred->id is set before
+     * it executes its startFunc.  The parent thread must ensure
+     * that thred->id is set before PR_CreateThread() returns.
+     * Both threads set thred->id while holding pt_book.ml and
+     * use thred->idSet to ensure thred->id is written only once.
+     */
+    if (!thred->idSet)
+    {
+        thred->id = id;
+        thred->idSet = PR_TRUE;
+    }
+    else
+    {
+        PR_ASSERT(pthread_equal(thred->id, id));
+    }
+
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+    thred->tid = tid;
+    PR_NotifyAllCondVar(pt_book.cv);
+#endif
+
+    /* If this is a GCABLE thread, set its state appropriately */
+    if (thred->suspend & PT_THREAD_SETGCABLE)
+	    thred->state |= PT_THREAD_GCABLE;
+    thred->suspend = 0;
+
+    thred->prev = pt_book.last;
+    if (pt_book.last)
+        pt_book.last->next = thred;
+    else
+        pt_book.first = thred;
+    thred->next = NULL;
+    pt_book.last = thred;
+    PR_Unlock(pt_book.ml);
+
+    thred->startFunc(thred->arg);  /* make visible to the client */
+
+    /* unhook the thread from the runtime */
+    PR_Lock(pt_book.ml);
+    /*
+     * At this moment, PR_CreateThread() may not have set thred->id yet.
+     * It is safe for a detached thread to free thred only after
+     * PR_CreateThread() has accessed thred->id and thred->idSet.
+     */
+    if (detached)
+    {
+        while (!thred->okToDelete)
+            PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
+    }
+
+    if (thred->state & PT_THREAD_SYSTEM)
+        pt_book.system -= 1;
+    else if (--pt_book.user == pt_book.this_many)
+        PR_NotifyAllCondVar(pt_book.cv);
+    if (NULL == thred->prev)
+        pt_book.first = thred->next;
+    else
+        thred->prev->next = thred->next;
+    if (NULL == thred->next)
+        pt_book.last = thred->prev;
+    else
+        thred->next->prev = thred->prev;
+    PR_Unlock(pt_book.ml);
+
+    /*
+    * Here we set the pthread's backpointer to the PRThread to NULL.
+    * Otherwise the destructor would get called eagerly as the thread
+    * returns to the pthread runtime. The joining thread would them be
+    * the proud possessor of a dangling reference. However, this is the
+    * last chance to delete the object if the thread is detached, so
+    * just let the destructor do the work.
+    */
+    if (PR_FALSE == detached)
+    {
+        /* Call TPD destructors on this thread. */
+        _PR_DestroyThreadPrivate(thred);
+        rv = pthread_setspecific(pt_book.key, NULL);
+        PR_ASSERT(0 == rv);
+    }
+
+    return NULL;
+}  /* _pt_root */
+
+static PRThread* pt_AttachThread(void)
+{
+    PRThread *thred = NULL;
+
+    /*
+     * NSPR must have been initialized when PR_AttachThread is called.
+     * We cannot have PR_AttachThread call implicit initialization
+     * because if multiple threads call PR_AttachThread simultaneously,
+     * NSPR may be initialized more than once.
+     * We can't call any function that calls PR_GetCurrentThread()
+     * either (e.g., PR_SetError()) as that will result in infinite
+     * recursion.
+     */
+    if (!_pr_initialized) return NULL;
+
+    /* PR_NEWZAP must not call PR_GetCurrentThread() */
+    thred = PR_NEWZAP(PRThread);
+    if (NULL != thred)
+    {
+        int rv;
+
+        thred->priority = PR_PRIORITY_NORMAL;
+        thred->id = pthread_self();
+        thred->idSet = PR_TRUE;
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+        thred->tid = gettid();
+#endif
+        rv = pthread_setspecific(pt_book.key, thred);
+        PR_ASSERT(0 == rv);
+
+        thred->state = PT_THREAD_GLOBAL | PT_THREAD_FOREIGN;
+        PR_Lock(pt_book.ml);
+
+        /* then put it into the list */
+        thred->prev = pt_book.last;
+        if (pt_book.last)
+            pt_book.last->next = thred;
+        else
+            pt_book.first = thred;
+        thred->next = NULL;
+        pt_book.last = thred;
+        PR_Unlock(pt_book.ml);
+
+    }
+    return thred;  /* may be NULL */
+}  /* pt_AttachThread */
+
+static PRThread* _PR_CreateThread(
+    PRThreadType type, void (*start)(void *arg),
+    void *arg, PRThreadPriority priority, PRThreadScope scope,
+    PRThreadState state, PRUint32 stackSize, PRBool isGCAble)
+{
+    int rv;
+    PRThread *thred;
+    pthread_attr_t tattr;
+
+    if (!_pr_initialized) _PR_ImplicitInitialization();
+
+    if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority)
+        priority = PR_PRIORITY_FIRST;
+    else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority)
+        priority = PR_PRIORITY_LAST;
+
+    rv = _PT_PTHREAD_ATTR_INIT(&tattr);
+    PR_ASSERT(0 == rv);
+
+    if (EPERM != pt_schedpriv)
+    {
+#if !defined(_PR_DCETHREADS) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+        struct sched_param schedule;
+#endif
+
+#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+        rv = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);
+        PR_ASSERT(0 == rv);
+#endif
+
+        /* Use the default scheduling policy */
+
+#if defined(_PR_DCETHREADS)
+        rv = pthread_attr_setprio(&tattr, pt_PriorityMap(priority));
+        PR_ASSERT(0 == rv);
+#elif defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+        rv = pthread_attr_getschedparam(&tattr, &schedule);
+        PR_ASSERT(0 == rv);
+        schedule.sched_priority = pt_PriorityMap(priority);
+        rv = pthread_attr_setschedparam(&tattr, &schedule);
+        PR_ASSERT(0 == rv);
+#ifdef NTO
+        rv = pthread_attr_setschedpolicy(&tattr, SCHED_RR); /* Round Robin */
+        PR_ASSERT(0 == rv);
+#endif
+#endif /* !defined(_PR_DCETHREADS) */
+    }
+
+    /*
+     * DCE threads can't set detach state before creating the thread.
+     * AIX can't set detach late. Why can't we all just get along?
+     */
+#if !defined(_PR_DCETHREADS)
+    rv = pthread_attr_setdetachstate(&tattr,
+        ((PR_JOINABLE_THREAD == state) ?
+            PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED));
+    PR_ASSERT(0 == rv);
+#endif /* !defined(_PR_DCETHREADS) */
+
+    /*
+     * If stackSize is 0, we use the default pthread stack size.
+     */
+    if (stackSize)
+    {
+#ifdef _MD_MINIMUM_STACK_SIZE
+        if (stackSize < _MD_MINIMUM_STACK_SIZE)
+            stackSize = _MD_MINIMUM_STACK_SIZE;
+#endif
+        rv = pthread_attr_setstacksize(&tattr, stackSize);
+        PR_ASSERT(0 == rv);
+    }
+
+    thred = PR_NEWZAP(PRThread);
+    if (NULL == thred)
+    {
+        PR_SetError(PR_OUT_OF_MEMORY_ERROR, errno);
+        goto done;
+    }
+    else
+    {
+        pthread_t id;
+
+        thred->arg = arg;
+        thred->startFunc = start;
+        thred->priority = priority;
+        if (PR_UNJOINABLE_THREAD == state)
+            thred->state |= PT_THREAD_DETACHED;
+
+        if (PR_LOCAL_THREAD == scope)
+        	scope = PR_GLOBAL_THREAD;
+			
+        if (PR_GLOBAL_BOUND_THREAD == scope) {
+#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+    		rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
+			if (rv) {
+				/*
+				 * system scope not supported
+				 */
+        		scope = PR_GLOBAL_THREAD;
+				/*
+				 * reset scope
+				 */
+    			rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS);
+    			PR_ASSERT(0 == rv);
+			}
+#endif
+		}
+        if (PR_GLOBAL_THREAD == scope)
+            thred->state |= PT_THREAD_GLOBAL;
+        else if (PR_GLOBAL_BOUND_THREAD == scope)
+            thred->state |= (PT_THREAD_GLOBAL | PT_THREAD_BOUND);
+		else	/* force it global */
+            thred->state |= PT_THREAD_GLOBAL;
+        if (PR_SYSTEM_THREAD == type)
+            thred->state |= PT_THREAD_SYSTEM;
+
+        thred->suspend =(isGCAble) ? PT_THREAD_SETGCABLE : 0;
+
+        thred->stack = PR_NEWZAP(PRThreadStack);
+        if (thred->stack == NULL) {
+            PRIntn oserr = errno;
+            PR_Free(thred);  /* all that work ... poof! */
+            PR_SetError(PR_OUT_OF_MEMORY_ERROR, oserr);
+            thred = NULL;  /* and for what? */
+            goto done;
+        }
+        thred->stack->stackSize = stackSize;
+        thred->stack->thr = thred;
+
+#ifdef PT_NO_SIGTIMEDWAIT
+        pthread_mutex_init(&thred->suspendResumeMutex,NULL);
+        pthread_cond_init(&thred->suspendResumeCV,NULL);
+#endif
+
+        /* make the thread counted to the rest of the runtime */
+        PR_Lock(pt_book.ml);
+        if (PR_SYSTEM_THREAD == type)
+            pt_book.system += 1;
+        else pt_book.user += 1;
+        PR_Unlock(pt_book.ml);
+
+        /*
+         * We pass a pointer to a local copy (instead of thred->id)
+         * to pthread_create() because who knows what wacky things
+         * pthread_create() may be doing to its argument.
+         */
+        rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
+
+#if !defined(_PR_DCETHREADS)
+        if (EPERM == rv)
+        {
+#if defined(IRIX)
+        	if (PR_GLOBAL_BOUND_THREAD == scope) {
+				/*
+				 * SCOPE_SYSTEM requires appropriate privilege
+				 * reset to process scope and try again
+				 */
+    			rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS);
+    			PR_ASSERT(0 == rv);
+            	thred->state &= ~PT_THREAD_BOUND;
+			}
+#else
+            /* Remember that we don't have thread scheduling privilege. */
+            pt_schedpriv = EPERM;
+            PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+                ("_PR_CreateThread: no thread scheduling privilege"));
+            /* Try creating the thread again without setting priority. */
+#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+            rv = pthread_attr_setinheritsched(&tattr, PTHREAD_INHERIT_SCHED);
+            PR_ASSERT(0 == rv);
+#endif
+#endif	/* IRIX */
+            rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
+        }
+#endif
+
+        if (0 != rv)
+        {
+#if defined(_PR_DCETHREADS)
+            PRIntn oserr = errno;
+#else
+            PRIntn oserr = rv;
+#endif
+            PR_Lock(pt_book.ml);
+            if (thred->state & PT_THREAD_SYSTEM)
+                pt_book.system -= 1;
+            else if (--pt_book.user == pt_book.this_many)
+                PR_NotifyAllCondVar(pt_book.cv);
+            PR_Unlock(pt_book.ml);
+
+            PR_Free(thred->stack);
+            PR_Free(thred);  /* all that work ... poof! */
+            PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, oserr);
+            thred = NULL;  /* and for what? */
+            goto done;
+        }
+
+        PR_Lock(pt_book.ml);
+        /*
+         * Both the parent thread and this new thread set thred->id.
+         * The parent thread must ensure that thred->id is set before
+         * PR_CreateThread() returns.  (See comments in _pt_root().)
+         */
+        if (!thred->idSet)
+        {
+            thred->id = id;
+            thred->idSet = PR_TRUE;
+        }
+        else
+        {
+            PR_ASSERT(pthread_equal(thred->id, id));
+        }
+
+        /*
+         * If the new thread is detached, tell it that PR_CreateThread() has
+         * accessed thred->id and thred->idSet so it's ok to delete thred.
+         */
+        if (PR_UNJOINABLE_THREAD == state)
+        {
+            thred->okToDelete = PR_TRUE;
+            PR_NotifyAllCondVar(pt_book.cv);
+        }
+        PR_Unlock(pt_book.ml);
+    }
+
+done:
+    rv = _PT_PTHREAD_ATTR_DESTROY(&tattr);
+    PR_ASSERT(0 == rv);
+
+    return thred;
+}  /* _PR_CreateThread */
+
+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, PR_FALSE);
+} /* PR_CreateThread */
+
+PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble(
+    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, PR_TRUE);
+}  /* PR_CreateThreadGCAble */
+
+PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thred)
+{
+    return thred->environment;
+}  /* GetExecutionEnvironment */
+ 
+PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thred, void *env)
+{
+    thred->environment = env;
+}  /* SetExecutionEnvironment */
+
+PR_IMPLEMENT(PRThread*) PR_AttachThread(
+    PRThreadType type, PRThreadPriority priority, PRThreadStack *stack)
+{
+    return PR_GetCurrentThread();
+}  /* PR_AttachThread */
+
+
+PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thred)
+{
+    int rv = -1;
+    void *result = NULL;
+    PR_ASSERT(thred != NULL);
+
+    if ((0xafafafaf == thred->state)
+    || (PT_THREAD_DETACHED == (PT_THREAD_DETACHED & thred->state))
+    || (PT_THREAD_FOREIGN == (PT_THREAD_FOREIGN & thred->state)))
+    {
+        /*
+         * This might be a bad address, but if it isn't, the state should
+         * either be an unjoinable thread or it's already had the object
+         * deleted. However, the client that called join on a detached
+         * thread deserves all the rath I can muster....
+         */
+        PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+        PR_LogPrint(
+            "PR_JoinThread: %p not joinable | already smashed\n", thred);
+    }
+    else
+    {
+        pthread_t id = thred->id;
+        rv = pthread_join(id, &result);
+        PR_ASSERT(rv == 0 && result == NULL);
+        if (0 == rv)
+        {
+#ifdef _PR_DCETHREADS
+            rv = pthread_detach(&id);
+            PR_ASSERT(0 == rv);
+#endif
+            /*
+             * PR_FALSE, because the thread already called the TPD
+             * destructors before exiting _pt_root.
+             */
+            _pt_thread_death_internal(thred, PR_FALSE);
+        }
+        else
+        {
+            PRErrorCode prerror;
+            switch (rv)
+            {
+                case EINVAL:  /* not a joinable thread */
+                case ESRCH:   /* no thread with given ID */
+                    prerror = PR_INVALID_ARGUMENT_ERROR;
+                    break;
+                case EDEADLK: /* a thread joining with itself */
+                    prerror = PR_DEADLOCK_ERROR;
+                    break;
+                default:
+                    prerror = PR_UNKNOWN_ERROR;
+                    break;
+            }
+            PR_SetError(prerror, rv);
+        }
+    }
+    return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+}  /* PR_JoinThread */
+
+PR_IMPLEMENT(void) PR_DetachThread(void)
+{
+    void *thred;
+    int rv;
+
+    _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
+    if (NULL == thred) return;
+    _pt_thread_death(thred);
+    rv = pthread_setspecific(pt_book.key, NULL);
+    PR_ASSERT(0 == rv);
+}  /* PR_DetachThread */
+
+PR_IMPLEMENT(PRThread*) PR_GetCurrentThread(void)
+{
+    void *thred;
+
+    if (!_pr_initialized) _PR_ImplicitInitialization();
+
+    _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
+    if (NULL == thred) thred = pt_AttachThread();
+    PR_ASSERT(NULL != thred);
+    return (PRThread*)thred;
+}  /* PR_GetCurrentThread */
+
+PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thred)
+{
+    return (thred->state & PT_THREAD_BOUND) ?
+        PR_GLOBAL_BOUND_THREAD : PR_GLOBAL_THREAD;
+}  /* PR_GetThreadScope() */
+
+PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thred)
+{
+    return (thred->state & PT_THREAD_SYSTEM) ?
+        PR_SYSTEM_THREAD : PR_USER_THREAD;
+}
+
+PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thred)
+{
+    return (thred->state & PT_THREAD_DETACHED) ?
+        PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD;
+}  /* PR_GetThreadState */
+
+PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thred)
+{
+    PR_ASSERT(thred != NULL);
+    return thred->priority;
+}  /* PR_GetThreadPriority */
+
+PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred, PRThreadPriority newPri)
+{
+    PRIntn rv = -1;
+
+    PR_ASSERT(NULL != thred);
+
+    if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)newPri)
+        newPri = PR_PRIORITY_FIRST;
+    else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)newPri)
+        newPri = PR_PRIORITY_LAST;
+
+#if defined(_PR_DCETHREADS)
+    rv = pthread_setprio(thred->id, pt_PriorityMap(newPri));
+    /* pthread_setprio returns the old priority */
+#elif defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+    if (EPERM != pt_schedpriv)
+    {
+        int policy;
+        struct sched_param schedule;
+
+        rv = pthread_getschedparam(thred->id, &policy, &schedule);
+        if(0 == rv) {
+			schedule.sched_priority = pt_PriorityMap(newPri);
+			rv = pthread_setschedparam(thred->id, policy, &schedule);
+			if (EPERM == rv)
+			{
+				pt_schedpriv = EPERM;
+				PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+					("PR_SetThreadPriority: no thread scheduling privilege"));
+			}
+		}
+		if (rv != 0)
+			rv = -1;
+    }
+#elif defined(_PR_NICE_PRIORITY_SCHEDULING)
+    PR_Lock(pt_book.ml);
+    while (thred->tid == 0)
+        PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
+    PR_Unlock(pt_book.ml);
+
+    errno = 0;
+    rv = getpriority(PRIO_PROCESS, 0);
+
+    /* Do not proceed unless we know the main thread's nice value. */
+    if (errno == 0) {
+        rv = setpriority(PRIO_PROCESS, thred->tid,
+                         pt_RelativePriority(rv, newPri));
+
+        if (rv == -1)
+        {
+            /* We don't set pt_schedpriv to EPERM in case errno == EPERM
+             * because adjusting the nice value might be permitted for certain
+             * ranges but not for others. */
+            PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+                ("PR_SetThreadPriority: setpriority failed with error %d",
+                 errno));
+        }
+    }
+#endif
+
+    thred->priority = newPri;
+}  /* PR_SetThreadPriority */
+
+PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thred)
+{
+    /*
+    ** If the target thread indicates that it's waiting,
+    ** find the condition and broadcast to it. Broadcast
+    ** since we don't know which thread (if there are more
+    ** than one). This sounds risky, but clients must
+    ** test their invariants when resumed from a wait and
+    ** I don't expect very many threads to be waiting on
+    ** a single condition and I don't expect interrupt to
+    ** be used very often.
+    **
+    ** I don't know why I thought this would work. Must have
+    ** been one of those weaker momements after I'd been
+    ** smelling the vapors.
+    **
+    ** Even with the followng changes it is possible that
+    ** the pointer to the condition variable is pointing
+    ** at a bogus value. Will the unerlying code detect
+    ** that?
+    */
+    PRCondVar *cv;
+    PR_ASSERT(NULL != thred);
+    if (NULL == thred) return PR_FAILURE;
+
+    thred->state |= PT_THREAD_ABORTED;
+
+    cv = thred->waiting;
+    if ((NULL != cv) && !thred->interrupt_blocked)
+    {
+        PRIntn rv;
+        (void)PR_ATOMIC_INCREMENT(&cv->notify_pending);
+        rv = pthread_cond_broadcast(&cv->cv);
+        PR_ASSERT(0 == rv);
+        if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending))
+            PR_DestroyCondVar(cv);
+    }
+    return PR_SUCCESS;
+}  /* PR_Interrupt */
+
+PR_IMPLEMENT(void) PR_ClearInterrupt(void)
+{
+    PRThread *me = PR_GetCurrentThread();
+    me->state &= ~PT_THREAD_ABORTED;
+}  /* PR_ClearInterrupt */
+
+PR_IMPLEMENT(void) PR_BlockInterrupt(void)
+{
+    PRThread *me = PR_GetCurrentThread();
+    _PT_THREAD_BLOCK_INTERRUPT(me);
+}  /* PR_BlockInterrupt */
+
+PR_IMPLEMENT(void) PR_UnblockInterrupt(void)
+{
+    PRThread *me = PR_GetCurrentThread();
+    _PT_THREAD_UNBLOCK_INTERRUPT(me);
+}  /* PR_UnblockInterrupt */
+
+PR_IMPLEMENT(PRStatus) PR_Yield(void)
+{
+    static PRBool warning = PR_TRUE;
+    if (warning) warning = _PR_Obsolete(
+        "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)");
+    return PR_Sleep(PR_INTERVAL_NO_WAIT);
+}
+
+PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime ticks)
+{
+    PRStatus rv = PR_SUCCESS;
+
+    if (!_pr_initialized) _PR_ImplicitInitialization();
+
+    if (PR_INTERVAL_NO_WAIT == ticks)
+    {
+        _PT_PTHREAD_YIELD();
+    }
+    else
+    {
+        PRCondVar *cv;
+        PRIntervalTime timein;
+
+        timein = PR_IntervalNow();
+        cv = PR_NewCondVar(_pr_sleeplock);
+        PR_ASSERT(cv != NULL);
+        PR_Lock(_pr_sleeplock);
+        do
+        {
+            PRIntervalTime now = PR_IntervalNow();
+            PRIntervalTime delta = now - timein;
+            if (delta > ticks) break;
+            rv = PR_WaitCondVar(cv, ticks - delta);
+        } while (PR_SUCCESS == rv);
+        PR_Unlock(_pr_sleeplock);
+        PR_DestroyCondVar(cv);
+    }
+    return rv;
+}  /* PR_Sleep */
+
+static void _pt_thread_death(void *arg)
+{
+    void *thred;
+    int rv;
+
+    _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
+    if (NULL == thred)
+    {
+        /*
+         * Have PR_GetCurrentThread return the expected value to the
+         * destructors.
+         */
+        rv = pthread_setspecific(pt_book.key, arg);
+        PR_ASSERT(0 == rv);
+    }
+
+    /* PR_TRUE for: call destructors */ 
+    _pt_thread_death_internal(arg, PR_TRUE);
+
+    if (NULL == thred)
+    {
+        rv = pthread_setspecific(pt_book.key, NULL);
+        PR_ASSERT(0 == rv);
+    }
+}
+
+static void _pt_thread_death_internal(void *arg, PRBool callDestructors)
+{
+    PRThread *thred = (PRThread*)arg;
+
+    if (thred->state & (PT_THREAD_FOREIGN|PT_THREAD_PRIMORD))
+    {
+        PR_Lock(pt_book.ml);
+        if (NULL == thred->prev)
+            pt_book.first = thred->next;
+        else
+            thred->prev->next = thred->next;
+        if (NULL == thred->next)
+            pt_book.last = thred->prev;
+        else
+            thred->next->prev = thred->prev;
+        PR_Unlock(pt_book.ml);
+    }
+    if (callDestructors)
+        _PR_DestroyThreadPrivate(thred);
+    PR_Free(thred->privateData);
+    if (NULL != thred->errorString)
+        PR_Free(thred->errorString);
+    if (NULL != thred->name)
+        PR_Free(thred->name);
+    PR_Free(thred->stack);
+    if (NULL != thred->syspoll_list)
+        PR_Free(thred->syspoll_list);
+#if defined(_PR_POLL_WITH_SELECT)
+    if (NULL != thred->selectfd_list)
+        PR_Free(thred->selectfd_list);
+#endif
+#if defined(DEBUG)
+    memset(thred, 0xaf, sizeof(PRThread));
+#endif /* defined(DEBUG) */
+    PR_Free(thred);
+}  /* _pt_thread_death */
+
+void _PR_InitThreads(
+    PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs)
+{
+    int rv;
+    PRThread *thred;
+
+    PR_ASSERT(priority == PR_PRIORITY_NORMAL);
+
+#ifdef _PR_NEED_PTHREAD_INIT
+    /*
+     * On BSD/OS (3.1 and 4.0), the pthread subsystem is lazily
+     * initialized, but pthread_self() fails to initialize
+     * pthreads and hence returns a null thread ID if invoked
+     * by the primordial thread before any other pthread call.
+     * So we explicitly initialize pthreads here.
+     */
+    pthread_init();
+#endif
+
+#if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+#if defined(FREEBSD)
+    {
+    pthread_attr_t attr;
+    int policy;
+    /* get the min and max priorities of the default policy */
+    pthread_attr_init(&attr);
+    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
+    pthread_attr_getschedpolicy(&attr, &policy);
+    pt_book.minPrio = sched_get_priority_min(policy);
+    PR_ASSERT(-1 != pt_book.minPrio);
+    pt_book.maxPrio = sched_get_priority_max(policy);
+    PR_ASSERT(-1 != pt_book.maxPrio);
+    pthread_attr_destroy(&attr);
+    }
+#else
+    /*
+    ** These might be function evaluations
+    */
+    pt_book.minPrio = PT_PRIO_MIN;
+    pt_book.maxPrio = PT_PRIO_MAX;
+#endif
+#endif
+    
+    PR_ASSERT(NULL == pt_book.ml);
+    pt_book.ml = PR_NewLock();
+    PR_ASSERT(NULL != pt_book.ml);
+    pt_book.cv = PR_NewCondVar(pt_book.ml);
+    PR_ASSERT(NULL != pt_book.cv);
+    thred = PR_NEWZAP(PRThread);
+    PR_ASSERT(NULL != thred);
+    thred->arg = NULL;
+    thred->startFunc = NULL;
+    thred->priority = priority;
+    thred->id = pthread_self();
+    thred->idSet = PR_TRUE;
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+    thred->tid = gettid();
+#endif
+
+    thred->state = (PT_THREAD_DETACHED | PT_THREAD_PRIMORD);
+    if (PR_SYSTEM_THREAD == type)
+    {
+        thred->state |= PT_THREAD_SYSTEM;
+        pt_book.system += 1;
+	    pt_book.this_many = 0;
+    }
+    else
+    {
+	    pt_book.user += 1;
+	    pt_book.this_many = 1;
+    }
+    thred->next = thred->prev = NULL;
+    pt_book.first = pt_book.last = thred;
+
+    thred->stack = PR_NEWZAP(PRThreadStack);
+    PR_ASSERT(thred->stack != NULL);
+    thred->stack->stackSize = 0;
+    thred->stack->thr = thred;
+	_PR_InitializeStack(thred->stack);
+
+    /*
+     * Create a key for our use to store a backpointer in the pthread
+     * to our PRThread object. This object gets deleted when the thread
+     * returns from its root in the case of a detached thread. Other
+     * threads delete the objects in Join.
+     *
+     * NB: The destructor logic seems to have a bug so it isn't used.
+     * NBB: Oh really? I'm going to give it a spin - AOF 19 June 1998.
+     * More info - the problem is that pthreads calls the destructor
+     * eagerly as the thread returns from its root, rather than lazily
+     * after the thread is joined. Therefore, threads that are joining
+     * and holding PRThread references are actually holding pointers to
+     * nothing.
+     */
+    rv = _PT_PTHREAD_KEY_CREATE(&pt_book.key, _pt_thread_death);
+    if (0 != rv)
+        PR_Assert("0 == rv", __FILE__, __LINE__);
+    pt_book.keyCreated = PR_TRUE;
+    rv = pthread_setspecific(pt_book.key, thred);
+    PR_ASSERT(0 == rv);
+}  /* _PR_InitThreads */
+
+#ifdef __GNUC__
+/*
+ * GCC supports the constructor and destructor attributes as of
+ * version 2.5.
+ */
+static void _PR_Fini(void) __attribute__ ((destructor));
+#elif defined(__SUNPRO_C)
+/*
+ * Sun Studio compiler
+ */
+#pragma fini(_PR_Fini)
+static void _PR_Fini(void);
+#elif defined(HPUX)
+/*
+ * Current versions of HP C compiler define __HP_cc.
+ * HP C compiler A.11.01.20 doesn't define __HP_cc.
+ */
+#if defined(__ia64) || defined(_LP64)
+#pragma FINI "_PR_Fini"
+static void _PR_Fini(void);
+#else
+/*
+ * Only HP-UX 10.x style initializers are supported in 32-bit links.
+ * Need to use the +I PR_HPUX10xInit linker option.
+ */
+#include <dl.h>
+
+static void _PR_Fini(void);
+
+void PR_HPUX10xInit(shl_t handle, int loading)
+{
+    /*
+     * This function is called when a shared library is loaded as well
+     * as when the shared library is unloaded.  Note that it may not
+     * be called when the user's program terminates.
+     *
+     * handle is the shl_load API handle for the shared library being
+     * initialized.
+     *
+     * loading is non-zero at startup and zero at termination.
+     */
+    if (loading) {
+	/* ... do some initializations ... */
+    } else {
+	_PR_Fini();
+    }
+}
+#endif
+#elif defined(AIX)
+/* Need to use the -binitfini::_PR_Fini linker option. */
+#endif
+
+void _PR_Fini(void)
+{
+    void *thred;
+    int rv;
+
+    if (!_pr_initialized) {
+        /* Either NSPR was never successfully initialized or 
+         * PR_Cleanup has been called already. */
+        if (pt_book.keyCreated)
+        {
+            rv = pthread_key_delete(pt_book.key);
+            PR_ASSERT(0 == rv);
+            pt_book.keyCreated = PR_FALSE;
+        }
+        return;
+    }
+
+    _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
+    if (NULL != thred)
+    {
+        /*
+         * PR_FALSE, because it is unsafe to call back to the 
+         * thread private data destructors at final cleanup.
+         */
+        _pt_thread_death_internal(thred, PR_FALSE);
+        rv = pthread_setspecific(pt_book.key, NULL);
+        PR_ASSERT(0 == rv);
+    }
+    rv = pthread_key_delete(pt_book.key);
+    PR_ASSERT(0 == rv);
+    pt_book.keyCreated = PR_FALSE;
+    /* TODO: free other resources used by NSPR */
+    /* _pr_initialized = PR_FALSE; */
+}  /* _PR_Fini */
+
+PR_IMPLEMENT(PRStatus) PR_Cleanup(void)
+{
+    PRThread *me = PR_GetCurrentThread();
+    int rv;
+    PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR"));
+    PR_ASSERT(me->state & PT_THREAD_PRIMORD);
+    if (me->state & PT_THREAD_PRIMORD)
+    {
+        PR_Lock(pt_book.ml);
+        while (pt_book.user > pt_book.this_many)
+            PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
+        if (me->state & PT_THREAD_SYSTEM)
+            pt_book.system -= 1;
+        else
+            pt_book.user -= 1;
+        PR_Unlock(pt_book.ml);
+
+        _PR_MD_EARLY_CLEANUP();
+
+        _PR_CleanupMW();
+        _PR_CleanupTime();
+        _PR_CleanupDtoa();
+        _PR_CleanupCallOnce();
+        _PR_ShutdownLinker();
+        _PR_LogCleanup();
+        _PR_CleanupNet();
+        /* Close all the fd's before calling _PR_CleanupIO */
+        _PR_CleanupIO();
+        _PR_CleanupCMon();
+
+        _pt_thread_death(me);
+        rv = pthread_setspecific(pt_book.key, NULL);
+        PR_ASSERT(0 == rv);
+        /*
+         * I am not sure if it's safe to delete the cv and lock here,
+         * since there may still be "system" threads around. If this
+         * call isn't immediately prior to exiting, then there's a
+         * problem.
+         */
+        if (0 == pt_book.system)
+        {
+            PR_DestroyCondVar(pt_book.cv); pt_book.cv = NULL;
+            PR_DestroyLock(pt_book.ml); pt_book.ml = NULL;
+        }
+        PR_DestroyLock(_pr_sleeplock);
+        _pr_sleeplock = NULL;
+        _PR_CleanupLayerCache();
+        _PR_CleanupEnv();
+#ifdef _PR_ZONE_ALLOCATOR
+        _PR_DestroyZones();
+#endif
+        _pr_initialized = PR_FALSE;
+        return PR_SUCCESS;
+    }
+    return PR_FAILURE;
+}  /* PR_Cleanup */
+
+PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status)
+{
+    _exit(status);
+}
+
+PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thred)
+{
+#if defined(_PR_DCETHREADS)
+    return (PRUint32)&thred->id;  /* this is really a sham! */
+#else
+    return (PRUint32)thred->id;  /* and I don't know what they will do with it */
+#endif
+}
+
+/*
+ * $$$
+ * The following two thread-to-processor affinity functions are not
+ * yet implemented for pthreads.  By the way, these functions should return
+ * PRStatus rather than PRInt32 to indicate the success/failure status.
+ * $$$
+ */
+
+PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask)
+{
+    return 0;  /* not implemented */
+}
+
+PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask )
+{
+    return 0;  /* not implemented */
+}
+
+PR_IMPLEMENT(void)
+PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg)
+{
+    thread->dump = dump;
+    thread->dumpArg = arg;
+}
+
+/* 
+ * Garbage collection support follows.
+ */
+
+#if defined(_PR_DCETHREADS)
+
+/*
+ * statics for Garbage Collection support.  We don't need to protect these
+ * signal masks since the garbage collector itself is protected by a lock
+ * and multiple threads will not be garbage collecting at the same time.
+ */
+static sigset_t javagc_vtalarm_sigmask;
+static sigset_t javagc_intsoff_sigmask;
+
+#else /* defined(_PR_DCETHREADS) */
+
+/* a bogus signal mask for forcing a timed wait */
+/* Not so bogus in AIX as we really do a sigwait */
+static sigset_t sigwait_set;
+
+static struct timespec onemillisec = {0, 1000000L};
+#ifndef PT_NO_SIGTIMEDWAIT
+static struct timespec hundredmillisec = {0, 100000000L};
+#endif
+
+static void suspend_signal_handler(PRIntn sig);
+
+#ifdef PT_NO_SIGTIMEDWAIT
+static void null_signal_handler(PRIntn sig);
+#endif
+
+#endif /* defined(_PR_DCETHREADS) */
+
+/*
+ * Linux pthreads use SIGUSR1 and SIGUSR2 internally, which
+ * conflict with the use of these two signals in our GC support.
+ * So we don't know how to support GC on Linux pthreads.
+ */
+static void init_pthread_gc_support(void)
+{
+#ifndef SYMBIAN
+    PRIntn rv;
+
+#if defined(_PR_DCETHREADS)
+	rv = sigemptyset(&javagc_vtalarm_sigmask);
+    PR_ASSERT(0 == rv);
+	rv = sigaddset(&javagc_vtalarm_sigmask, SIGVTALRM);
+    PR_ASSERT(0 == rv);
+#else  /* defined(_PR_DCETHREADS) */
+	{
+	    struct sigaction sigact_usr2;
+
+	    sigact_usr2.sa_handler = suspend_signal_handler;
+	    sigact_usr2.sa_flags = SA_RESTART;
+	    sigemptyset (&sigact_usr2.sa_mask);
+
+        rv = sigaction (SIGUSR2, &sigact_usr2, NULL);
+        PR_ASSERT(0 == rv);
+
+        sigemptyset (&sigwait_set);
+#if defined(PT_NO_SIGTIMEDWAIT)
+        sigaddset (&sigwait_set, SIGUSR1);
+#else
+        sigaddset (&sigwait_set, SIGUSR2);
+#endif  /* defined(PT_NO_SIGTIMEDWAIT) */
+	}
+#if defined(PT_NO_SIGTIMEDWAIT)
+	{
+	    struct sigaction sigact_null;
+	    sigact_null.sa_handler = null_signal_handler;
+	    sigact_null.sa_flags = SA_RESTART;
+	    sigemptyset (&sigact_null.sa_mask);
+        rv = sigaction (SIGUSR1, &sigact_null, NULL);
+	    PR_ASSERT(0 ==rv); 
+    }
+#endif  /* defined(PT_NO_SIGTIMEDWAIT) */
+#endif /* defined(_PR_DCETHREADS) */
+#endif /* SYMBIAN */
+}
+
+PR_IMPLEMENT(void) PR_SetThreadGCAble(void)
+{
+    PR_Lock(pt_book.ml);
+	PR_GetCurrentThread()->state |= PT_THREAD_GCABLE;
+    PR_Unlock(pt_book.ml);
+}
+
+PR_IMPLEMENT(void) PR_ClearThreadGCAble(void)
+{
+    PR_Lock(pt_book.ml);
+	PR_GetCurrentThread()->state &= (~PT_THREAD_GCABLE);
+    PR_Unlock(pt_book.ml);
+}
+
+#if defined(DEBUG)
+static PRBool suspendAllOn = PR_FALSE;
+#endif
+
+static PRBool suspendAllSuspended = PR_FALSE;
+
+PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg)
+{
+    PRIntn count = 0;
+    PRStatus rv = PR_SUCCESS;
+    PRThread* thred = pt_book.first;
+
+#if defined(DEBUG) || defined(FORCE_PR_ASSERT)
+#if !defined(_PR_DCETHREADS)
+    PRThread *me = PR_GetCurrentThread();
+#endif
+#endif
+
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_EnumerateThreads\n"));
+    /*
+     * $$$
+     * Need to suspend all threads other than me before doing this.
+     * This is really a gross and disgusting thing to do. The only
+     * good thing is that since all other threads are suspended, holding
+     * the lock during a callback seems like child's play.
+     * $$$
+     */
+    PR_ASSERT(suspendAllOn);
+
+    while (thred != NULL)
+    {
+        /* 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".
+         */
+        PRThread* next = thred->next;
+
+        if (_PT_IS_GCABLE_THREAD(thred))
+        {
+#if !defined(_PR_DCETHREADS)
+            PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED));
+#endif
+            PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
+                   ("In PR_EnumerateThreads callback thread %p thid = %X\n", 
+                    thred, thred->id));
+
+            rv = func(thred, count++, arg);
+            if (rv != PR_SUCCESS)
+                return rv;
+        }
+        thred = next;
+    }
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
+	   ("End PR_EnumerateThreads count = %d \n", count));
+    return rv;
+}  /* PR_EnumerateThreads */
+
+/*
+ * PR_SuspendAll and PR_ResumeAll are called during garbage collection.  The strategy 
+ * we use is to send a SIGUSR2 signal to every gc able thread that we intend to suspend.
+ * The signal handler will record the stack pointer and will block until resumed by
+ * the resume call.  Since the signal handler is the last routine called for the
+ * suspended thread, the stack pointer will also serve as a place where all the
+ * registers have been saved on the stack for the previously executing routines.
+ *
+ * Through global variables, we also make sure that PR_Suspend and PR_Resume does not
+ * proceed until the thread is suspended or resumed.
+ */
+
+#if !defined(_PR_DCETHREADS)
+
+/*
+ * In the signal handler, we can not use condition variable notify or wait.
+ * This does not work consistently across all pthread platforms.  We also can not 
+ * use locking since that does not seem to work reliably across platforms.
+ * Only thing we can do is yielding while testing for a global condition
+ * to change.  This does work on pthread supported platforms.  We may have
+ * to play with priortities if there are any problems detected.
+ */
+
+ /* 
+  * In AIX, you cannot use ANY pthread calls in the signal handler except perhaps
+  * pthread_yield. But that is horribly inefficient. Hence we use only sigwait, no
+  * sigtimedwait is available. We need to use another user signal, SIGUSR1. Actually
+  * SIGUSR1 is also used by exec in Java. So our usage here breaks the exec in Java,
+  * for AIX. You cannot use pthread_cond_wait or pthread_delay_np in the signal
+  * handler as all synchronization mechanisms just break down. 
+  */
+
+#if defined(PT_NO_SIGTIMEDWAIT)
+static void null_signal_handler(PRIntn sig)
+{
+	return;
+}
+#endif
+
+static void suspend_signal_handler(PRIntn sig)
+{
+	PRThread *me = PR_GetCurrentThread();
+
+	PR_ASSERT(me != NULL);
+	PR_ASSERT(_PT_IS_GCABLE_THREAD(me));
+	PR_ASSERT((me->suspend & PT_THREAD_SUSPENDED) == 0);
+
+	PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
+        ("Begin suspend_signal_handler thred %p thread id = %X\n", 
+		me, me->id));
+
+	/*
+	 * save stack pointer
+	 */
+	me->sp = &me;
+
+	/* 
+	   At this point, the thread's stack pointer has been saved,
+	   And it is going to enter a wait loop until it is resumed.
+	   So it is _really_ suspended 
+	*/
+
+	me->suspend |= PT_THREAD_SUSPENDED;
+
+	/*
+	 * now, block current thread
+	 */
+#if defined(PT_NO_SIGTIMEDWAIT)
+	pthread_cond_signal(&me->suspendResumeCV);
+	while (me->suspend & PT_THREAD_SUSPENDED)
+	{
+#if !defined(FREEBSD) && !defined(NETBSD) && !defined(OPENBSD) \
+    && !defined(BSDI) && !defined(UNIXWARE) \
+    && !defined(DARWIN) && !defined(RISCOS) \
+    && !defined(SYMBIAN) /*XXX*/
+        PRIntn rv;
+	    sigwait(&sigwait_set, &rv);
+#endif
+	}
+	me->suspend |= PT_THREAD_RESUMED;
+	pthread_cond_signal(&me->suspendResumeCV);
+#else /* defined(PT_NO_SIGTIMEDWAIT) */
+	while (me->suspend & PT_THREAD_SUSPENDED)
+	{
+		PRIntn rv = sigtimedwait(&sigwait_set, NULL, &hundredmillisec);
+    	PR_ASSERT(-1 == rv);
+	}
+	me->suspend |= PT_THREAD_RESUMED;
+#endif
+
+    /*
+     * At this point, thread has been resumed, so set a global condition.
+     * The ResumeAll needs to know that this has really been resumed. 
+     * So the signal handler sets a flag which PR_ResumeAll will reset. 
+     * The PR_ResumeAll must reset this flag ...
+     */
+
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
+        ("End suspend_signal_handler thred = %p tid = %X\n", me, me->id));
+}  /* suspend_signal_handler */
+
+static void pt_SuspendSet(PRThread *thred)
+{
+    PRIntn rv;
+
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
+	   ("pt_SuspendSet thred %p thread id = %X\n", thred, thred->id));
+
+
+    /*
+     * Check the thread state and signal the thread to suspend
+     */
+
+    PR_ASSERT((thred->suspend & PT_THREAD_SUSPENDED) == 0);
+
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
+	   ("doing pthread_kill in pt_SuspendSet thred %p tid = %X\n",
+	   thred, thred->id));
+#if defined(SYMBIAN)
+    /* All signal group functions are not implemented in Symbian OS */
+    rv = 0;
+#else
+    rv = pthread_kill (thred->id, SIGUSR2);
+#endif
+    PR_ASSERT(0 == rv);
+}
+
+static void pt_SuspendTest(PRThread *thred)
+{
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
+	   ("Begin pt_SuspendTest thred %p thread id = %X\n", thred, thred->id));
+
+
+    /*
+     * Wait for the thread to be really suspended. This happens when the
+     * suspend signal handler stores the stack pointer and sets the state
+     * to suspended. 
+     */
+
+#if defined(PT_NO_SIGTIMEDWAIT)
+    pthread_mutex_lock(&thred->suspendResumeMutex);
+    while ((thred->suspend & PT_THREAD_SUSPENDED) == 0)
+    {
+	    pthread_cond_timedwait(
+	        &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec);
+	}
+	pthread_mutex_unlock(&thred->suspendResumeMutex);
+#else
+    while ((thred->suspend & PT_THREAD_SUSPENDED) == 0)
+    {
+		PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
+    	PR_ASSERT(-1 == rv);
+	}
+#endif
+
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+        ("End pt_SuspendTest thred %p tid %X\n", thred, thred->id));
+}  /* pt_SuspendTest */
+
+static void pt_ResumeSet(PRThread *thred)
+{
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
+	   ("pt_ResumeSet thred %p thread id = %X\n", thred, thred->id));
+
+    /*
+     * Clear the global state and set the thread state so that it will
+     * continue past yield loop in the suspend signal handler
+     */
+
+    PR_ASSERT(thred->suspend & PT_THREAD_SUSPENDED);
+
+
+    thred->suspend &= ~PT_THREAD_SUSPENDED;
+
+#if defined(PT_NO_SIGTIMEDWAIT)
+#if defined(SYMBIAN) 
+	/* All signal group functions are not implemented in Symbian OS */
+#else
+	pthread_kill(thred->id, SIGUSR1);
+#endif
+#endif
+
+}  /* pt_ResumeSet */
+
+static void pt_ResumeTest(PRThread *thred)
+{
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
+	   ("Begin pt_ResumeTest thred %p thread id = %X\n", thred, thred->id));
+
+    /*
+     * Wait for the threads resume state to change
+     * to indicate it is really resumed 
+     */
+#if defined(PT_NO_SIGTIMEDWAIT)
+    pthread_mutex_lock(&thred->suspendResumeMutex);
+    while ((thred->suspend & PT_THREAD_RESUMED) == 0)
+    {
+	    pthread_cond_timedwait(
+	        &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec);
+    }
+    pthread_mutex_unlock(&thred->suspendResumeMutex);
+#else
+    while ((thred->suspend & PT_THREAD_RESUMED) == 0) {
+		PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
+    	PR_ASSERT(-1 == rv);
+	}
+#endif
+
+    thred->suspend &= ~PT_THREAD_RESUMED;
+
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, (
+        "End pt_ResumeTest thred %p tid %X\n", thred, thred->id));
+}  /* pt_ResumeTest */
+
+static pthread_once_t pt_gc_support_control = PTHREAD_ONCE_INIT;
+
+PR_IMPLEMENT(void) PR_SuspendAll(void)
+{
+#ifdef DEBUG
+    PRIntervalTime stime, etime;
+#endif
+    PRThread* thred = pt_book.first;
+    PRThread *me = PR_GetCurrentThread();
+    int rv;
+
+    rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support);
+    PR_ASSERT(0 == rv);
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n"));
+    /*
+     * Stop all threads which are marked GC able.
+     */
+    PR_Lock(pt_book.ml);
+#ifdef DEBUG
+    suspendAllOn = PR_TRUE;
+    stime = PR_IntervalNow();
+#endif
+    while (thred != NULL)
+    {
+	    if ((thred != me) && _PT_IS_GCABLE_THREAD(thred))
+    		pt_SuspendSet(thred);
+        thred = thred->next;
+    }
+
+    /* Wait till they are really suspended */
+    thred = pt_book.first;
+    while (thred != NULL)
+    {
+	    if ((thred != me) && _PT_IS_GCABLE_THREAD(thred))
+            pt_SuspendTest(thred);
+        thred = thred->next;
+    }
+
+    suspendAllSuspended = PR_TRUE;
+
+#ifdef DEBUG
+    etime = PR_IntervalNow();
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,\
+        ("End PR_SuspendAll (time %dms)\n",
+        PR_IntervalToMilliseconds(etime - stime)));
+#endif
+}  /* PR_SuspendAll */
+
+PR_IMPLEMENT(void) PR_ResumeAll(void)
+{
+#ifdef DEBUG
+    PRIntervalTime stime, etime;
+#endif
+    PRThread* thred = pt_book.first;
+    PRThread *me = PR_GetCurrentThread();
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n"));
+    /*
+     * Resume all previously suspended GC able threads.
+     */
+    suspendAllSuspended = PR_FALSE;
+#ifdef DEBUG
+    stime = PR_IntervalNow();
+#endif
+
+    while (thred != NULL)
+    {
+	    if ((thred != me) && _PT_IS_GCABLE_THREAD(thred))
+    	    pt_ResumeSet(thred);
+        thred = thred->next;
+    }
+
+    thred = pt_book.first;
+    while (thred != NULL)
+    {
+	    if ((thred != me) && _PT_IS_GCABLE_THREAD(thred))
+    	    pt_ResumeTest(thred);
+        thred = thred->next;
+    }
+
+    PR_Unlock(pt_book.ml);
+#ifdef DEBUG
+    suspendAllOn = PR_FALSE;
+    etime = PR_IntervalNow();
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+        ("End PR_ResumeAll (time %dms)\n",
+        PR_IntervalToMilliseconds(etime - stime)));
+#endif
+}  /* PR_ResumeAll */
+
+/* Return the stack pointer for the given thread- used by the GC */
+PR_IMPLEMENT(void *)PR_GetSP(PRThread *thred)
+{
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
+	    ("in PR_GetSP thred %p thid = %X, sp = %p\n", 
+	    thred, thred->id, thred->sp));
+    return thred->sp;
+}  /* PR_GetSP */
+
+#else /* !defined(_PR_DCETHREADS) */
+
+static pthread_once_t pt_gc_support_control = pthread_once_init;
+
+/*
+ * For DCE threads, there is no pthread_kill or a way of suspending or resuming a
+ * particular thread.  We will just disable the preemption (virtual timer alarm) and
+ * let the executing thread finish the garbage collection.  This stops all other threads
+ * (GC able or not) and is very inefficient but there is no other choice.
+ */
+PR_IMPLEMENT(void) PR_SuspendAll()
+{
+    PRIntn rv;
+
+    rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support);
+    PR_ASSERT(0 == rv);  /* returns -1 on failure */
+#ifdef DEBUG
+    suspendAllOn = PR_TRUE;
+#endif
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n"));
+    /* 
+     * turn off preemption - i.e add virtual alarm signal to the set of 
+     * blocking signals 
+     */
+    rv = sigprocmask(
+        SIG_BLOCK, &javagc_vtalarm_sigmask, &javagc_intsoff_sigmask);
+    PR_ASSERT(0 == rv);
+    suspendAllSuspended = PR_TRUE;
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_SuspendAll\n"));
+}  /* PR_SuspendAll */
+
+PR_IMPLEMENT(void) PR_ResumeAll()
+{
+    PRIntn rv;
+    
+    suspendAllSuspended = PR_FALSE;
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n"));
+    /* turn on preemption - i.e re-enable virtual alarm signal */
+
+    rv = sigprocmask(SIG_SETMASK, &javagc_intsoff_sigmask, (sigset_t *)NULL);
+    PR_ASSERT(0 == rv);
+#ifdef DEBUG
+    suspendAllOn = PR_FALSE;
+#endif
+
+    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_ResumeAll\n"));
+}  /* PR_ResumeAll */
+
+/* Return the stack pointer for the given thread- used by the GC */
+PR_IMPLEMENT(void*)PR_GetSP(PRThread *thred)
+{
+	pthread_t tid = thred->id;
+	char *thread_tcb, *top_sp;
+
+	/*
+	 * For HPUX DCE threads, pthread_t is a struct with the
+	 * following three fields (see pthread.h, dce/cma.h):
+	 *     cma_t_address       field1;
+	 *     short int           field2;
+	 *     short int           field3;
+	 * where cma_t_address is typedef'd to be either void*
+	 * or char*.
+	 */
+	PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_GetSP\n"));
+	thread_tcb = (char*)tid.field1;
+	top_sp = *(char**)(thread_tcb + 128);
+	PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_GetSP %p \n", top_sp));
+	return top_sp;
+}  /* PR_GetSP */
+
+#endif /* !defined(_PR_DCETHREADS) */
+
+PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char *name)
+{
+    PRThread *thread;
+    size_t nameLen;
+    int result;
+
+    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);
+
+#if defined(OPENBSD) || defined(FREEBSD)
+    result = pthread_set_name_np(thread->id, name);
+#else /* not BSD */
+    /*
+     * On OSX, pthread_setname_np is only available in 10.6 or later, so test
+     * for it at runtime.  It also may not be available on all linux distros.
+     */
+#if defined(DARWIN)
+    int (*dynamic_pthread_setname_np)(const char*);
+#else
+    int (*dynamic_pthread_setname_np)(pthread_t, const char*);
+#endif
+
+    *(void**)(&dynamic_pthread_setname_np) =
+        dlsym(RTLD_DEFAULT, "pthread_setname_np");
+    if (!dynamic_pthread_setname_np)
+        return PR_SUCCESS;
+
+    /*
+     * The 15-character name length limit is an experimentally determined
+     * length of a null-terminated string that most linux distros and OS X
+     * accept as an argument to pthread_setname_np.  Otherwise the E2BIG
+     * error is returned by the function.
+     */
+#define SETNAME_LENGTH_CONSTRAINT 15
+#define SETNAME_FRAGMENT1_LENGTH (SETNAME_LENGTH_CONSTRAINT >> 1)
+#define SETNAME_FRAGMENT2_LENGTH \
+    (SETNAME_LENGTH_CONSTRAINT - SETNAME_FRAGMENT1_LENGTH - 1)
+    char name_dup[SETNAME_LENGTH_CONSTRAINT + 1];
+    if (nameLen > SETNAME_LENGTH_CONSTRAINT) {
+        memcpy(name_dup, name, SETNAME_FRAGMENT1_LENGTH);
+        name_dup[SETNAME_FRAGMENT1_LENGTH] = '~';
+        /* Note that this also copies the null terminator. */
+        memcpy(name_dup + SETNAME_FRAGMENT1_LENGTH + 1,
+               name + nameLen - SETNAME_FRAGMENT2_LENGTH,
+               SETNAME_FRAGMENT2_LENGTH + 1);
+        name = name_dup;
+    }
+
+#if defined(DARWIN)
+    result = dynamic_pthread_setname_np(name);
+#else
+    result = dynamic_pthread_setname_np(thread->id, name);
+#endif
+#endif /* not BSD */
+
+    if (result) {
+        PR_SetError(PR_UNKNOWN_ERROR, result);
+        return PR_FAILURE;
+    }
+    return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread)
+{
+    if (!thread)
+        return NULL;
+    return thread->name;
+}
+
+#endif  /* defined(_PR_PTHREADS) || defined(_PR_DCETHREADS) */
+
+/* ptthread.c */
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)