Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  ptthread.c   Sprache: C

 
/* -*- 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)

#  include "prlog.h"
#  include "primpl.h"
#  include "prpdce.h"

#  include <pthread.h>
#  include <unistd.h>
#  include <string.h>
#  include <signal.h>
#  include <dlfcn.h>

#  if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY)
#    include <pthread_np.h>
#  endif

#  if defined(ANDROID)
#    include <sys/prctl.h>
#  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 _POSIX_THREAD_PRIORITY_SCHEDULING > 0
  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 _POSIX_THREAD_PRIORITY_SCHEDULING > 0
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

  /* 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 _POSIX_THREAD_PRIORITY_SCHEDULING > 0
    struct sched_param schedule;
#  endif

#  if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
    rv = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);
    PR_ASSERT(0 == rv);
#  endif

    /* Use the default scheduling policy */

#  if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
    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 /* _POSIX_THREAD_PRIORITY_SCHEDULING > 0 */
  }

  rv = pthread_attr_setdetachstate(
      &tattr, ((PR_JOINABLE_THREAD == state) ? PTHREAD_CREATE_JOINABLE
                                             : PTHREAD_CREATE_DETACHED));
  PR_ASSERT(0 == rv);

  /*
   * 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 _POSIX_THREAD_PRIORITY_SCHEDULING > 0
      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 (EPERM == rv) {
      /* 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 _POSIX_THREAD_PRIORITY_SCHEDULING > 0
      rv = pthread_attr_setinheritsched(&tattr, PTHREAD_INHERIT_SCHED);
      PR_ASSERT(0 == rv);
#  endif
      rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
    }

    if (0 != rv) {
      PRIntn oserr = rv;
      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) {
      /*
       * 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;

  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 _POSIX_THREAD_PRIORITY_SCHEDULING > 0
  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));
    }
  }
#  else
  (void)rv; /* rv is unused */
#  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 _POSIX_THREAD_PRIORITY_SCHEDULING > 0
#    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.
 */

#    if defined(DARWIN)
/*
 * The dynamic linker on OSX doesn't execute __attribute__((destructor))
 * destructors in the right order wrt non-__attribute((destructor)) destructors
 * in other libraries. So use atexit() instead, which does.
 * See https://bugzilla.mozilla.org/show_bug.cgi?id=1399746#c99
 */

static void _PR_Fini(void);

__attribute__((constructor)) static void _register_PR_Fini() {
  atexit(_PR_Fini);
}
#    else
static void _PR_Fini(void) __attribute__((destructor));
#    endif

#  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) {
  return (PRUint32)thred->id; /* and I don't know what they will do with it */
}

/*
 * $$$
 * 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.
 */


/* 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

/*
 * 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) {
  PRIntn rv;

  {
    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) */
}

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)
  PRThread* me = PR_GetCurrentThread();
#  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)) {
      PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED));
      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.
 */


/*
 * 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(DARWIN) && !defined(RISCOS)
    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));
  rv = pthread_kill(thred->id, SIGUSR2);
  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)
  pthread_kill(thred->id, SIGUSR1);
#  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 */

PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char* name) {
  PRThread* thread;
  size_t nameLen;
  int result = 0;

  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) || defined(DRAGONFLY)
  pthread_set_name_np(thread->id, name);
#  elif defined(ANDROID)
  prctl(PR_SET_NAME, (unsigned long)(name));
#  elif defined(NETBSD)
  result = pthread_setname_np(thread->id, "%s", (void*)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;
  }

#    if defined(DARWIN)
  /* Mac OS X has a length limit of 63 characters, but there is no API
   * exposing it.
   */

#      define SETNAME_LENGTH_CONSTRAINT 63
#    else
  /*
   * The 15-character name length limit is an experimentally determined
   * length of a null-terminated string that most linux distros accept
   * as an argument to pthread_setname_np.  Otherwise the E2BIG
   * error is returned by the function.
   */

#      define SETNAME_LENGTH_CONSTRAINT 15
#    endif
#    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) */

/* ptthread.c */

Messung V0.5
C=86 H=93 G=89

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge