Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/nsprpub/pr/src/threads/combined/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 50 kB image not shown  

Quelle  pruthr.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/. */


#include "primpl.h"
#include <signal.h>
#include <string.h>

#if defined(WIN95)
/*
** Some local variables report warnings on Win95 because the code paths
** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
** The pragma suppresses the warning.
**
*/

#  pragma warning(disable : 4101)
#endif

/* _pr_activeLock protects the following global variables */
PRLock* _pr_activeLock;
PRInt32 _pr_primordialExitCount;   /* In PR_Cleanup(), the primordial thread
                                    * waits until all other user (non-system)
                                    * threads have terminated before it exits.
                                    * So whenever we decrement _pr_userActive,
                                    * it is compared with
                                    * _pr_primordialExitCount.
                                    * If the primordial thread is a system
                                    * thread, then _pr_primordialExitCount
                                    * is 0.  If the primordial thread is
                                    * itself a user thread, then
                                    * _pr_primordialThread is 1.
                                    */

PRCondVar* _pr_primordialExitCVar; /* When _pr_userActive is decremented to
                                    * _pr_primordialExitCount, this condition
                                    * variable is notified.
                                    */


PRLock* _pr_deadQLock;
PRUint32 _pr_numNativeDead;
PRUint32 _pr_numUserDead;
PRCList _pr_deadNativeQ;
PRCList _pr_deadUserQ;

PRUint32 _pr_join_counter;

PRUint32 _pr_local_threads;
PRUint32 _pr_global_threads;

PRBool suspendAllOn = PR_FALSE;
PRThread* suspendAllThread = NULL;

extern PRCList _pr_active_global_threadQ;
extern PRCList _pr_active_local_threadQ;

static void _PR_DecrActiveThreadCount(PRThread* thread);
static PRThread* _PR_AttachThread(PRThreadType, PRThreadPriority,
                                  PRThreadStack*);
static void _PR_InitializeNativeStack(PRThreadStack* ts);
static void _PR_InitializeRecycledThread(PRThread* thread);
static void _PR_UserRunThread(void);

void _PR_InitThreads(PRThreadType type, PRThreadPriority priority,
                     PRUintn maxPTDs) {
  PRThread* thread;
  PRThreadStack* stack;

  PR_ASSERT(priority == PR_PRIORITY_NORMAL);

  _pr_terminationCVLock = PR_NewLock();
  _pr_activeLock = PR_NewLock();

#ifndef HAVE_CUSTOM_USER_THREADS
  stack = PR_NEWZAP(PRThreadStack);
#  ifdef HAVE_STACK_GROWING_UP
  stack->stackTop =
      (char*)((((PRWord)&type) >> _pr_pageShift) << _pr_pageShift);
#  else
#    if defined(SOLARIS)
  stack->stackTop = (char*)&thread;
#    else
  stack->stackTop =
      (char*)((((PRWord)&type + _pr_pageSize - 1) >> _pr_pageShift)
              << _pr_pageShift);
#    endif
#  endif
#else
  /* If stack is NULL, we're using custom user threads like NT fibers. */
  stack = PR_NEWZAP(PRThreadStack);
  if (stack) {
    stack->stackSize = 0;
    _PR_InitializeNativeStack(stack);
  }
#endif /* HAVE_CUSTOM_USER_THREADS */

  thread = _PR_AttachThread(type, priority, stack);
  if (thread) {
    _PR_MD_SET_CURRENT_THREAD(thread);

    if (type == PR_SYSTEM_THREAD) {
      thread->flags = _PR_SYSTEM;
      _pr_systemActive++;
      _pr_primordialExitCount = 0;
    } else {
      _pr_userActive++;
      _pr_primordialExitCount = 1;
    }
    thread->no_sched = 1;
    _pr_primordialExitCVar = PR_NewCondVar(_pr_activeLock);
  }

  if (!thread) {
    PR_Abort();
  }
#ifdef _PR_LOCAL_THREADS_ONLY
  thread->flags |= _PR_PRIMORDIAL;
#else
  thread->flags |= _PR_PRIMORDIAL | _PR_GLOBAL_SCOPE;
#endif

  /*
   * Needs _PR_PRIMORDIAL flag set before calling
   * _PR_MD_INIT_THREAD()
   */

  if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
    /*
     * XXX do what?
     */

  }

  if (_PR_IS_NATIVE_THREAD(thread)) {
    PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ());
    _pr_global_threads++;
  } else {
    PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ());
    _pr_local_threads++;
  }

  _pr_recycleThreads = 0;
  _pr_deadQLock = PR_NewLock();
  _pr_numNativeDead = 0;
  _pr_numUserDead = 0;
  PR_INIT_CLIST(&_pr_deadNativeQ);
  PR_INIT_CLIST(&_pr_deadUserQ);
}

void _PR_CleanupThreads(void) {
  if (_pr_terminationCVLock) {
    PR_DestroyLock(_pr_terminationCVLock);
    _pr_terminationCVLock = NULL;
  }
  if (_pr_activeLock) {
    PR_DestroyLock(_pr_activeLock);
    _pr_activeLock = NULL;
  }
  if (_pr_primordialExitCVar) {
    PR_DestroyCondVar(_pr_primordialExitCVar);
    _pr_primordialExitCVar = NULL;
  }
  /* TODO _pr_dead{Native,User}Q need to be deleted */
  if (_pr_deadQLock) {
    PR_DestroyLock(_pr_deadQLock);
    _pr_deadQLock = NULL;
  }
}

/*
** Initialize a stack for a native thread
*/

static void _PR_InitializeNativeStack(PRThreadStack* ts) {
  if (ts && (ts->stackTop == 0)) {
    ts->allocSize = ts->stackSize;

    /*
    ** Setup stackTop and stackBottom values.
    */

#ifdef HAVE_STACK_GROWING_UP
    ts->allocBase = (char*)((((PRWord)&ts) >> _pr_pageShift) << _pr_pageShift);
    ts->stackBottom = ts->allocBase + ts->stackSize;
    ts->stackTop = ts->allocBase;
#else
    ts->allocBase = (char*)((((PRWord)&ts + _pr_pageSize - 1) >> _pr_pageShift)
                            << _pr_pageShift);
    ts->stackTop = ts->allocBase;
    ts->stackBottom = ts->allocBase - ts->stackSize;
#endif
  }
}

void _PR_NotifyJoinWaiters(PRThread* thread) {
  /*
  ** Handle joinable threads.  Change the state to waiting for join.
  ** Remove from our run Q and put it on global waiting to join Q.
  ** Notify on our "termination" condition variable so that joining
  ** thread will know about our termination.  Switch our context and
  ** come back later on to continue the cleanup.
  */

  PR_ASSERT(thread == _PR_MD_CURRENT_THREAD());
  if (thread->term != NULL) {
    PR_Lock(_pr_terminationCVLock);
    _PR_THREAD_LOCK(thread);
    thread->state = _PR_JOIN_WAIT;
    if (!_PR_IS_NATIVE_THREAD(thread)) {
      _PR_MISCQ_LOCK(thread->cpu);
      _PR_ADD_JOINQ(thread, thread->cpu);
      _PR_MISCQ_UNLOCK(thread->cpu);
    }
    _PR_THREAD_UNLOCK(thread);
    PR_NotifyCondVar(thread->term);
    PR_Unlock(_pr_terminationCVLock);
    _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT);
    PR_ASSERT(thread->state != _PR_JOIN_WAIT);
  }
}

/*
 * Zero some of the data members of a recycled thread.
 *
 * Note that we can do this either when a dead thread is added to
 * the dead thread queue or when it is reused.  Here, we are doing
 * this lazily, when the thread is reused in _PR_CreateThread().
 */

static void _PR_InitializeRecycledThread(PRThread* thread) {
  /*
   * Assert that the following data members are already zeroed
   * by _PR_CleanupThread().
   */

#ifdef DEBUG
  if (thread->privateData) {
    unsigned int i;
    for (i = 0; i < thread->tpdLength; i++) {
      PR_ASSERT(thread->privateData[i] == NULL);
    }
  }
#endif
  PR_ASSERT(thread->dumpArg == 0 && thread->dump == 0);
  PR_ASSERT(thread->errorString == 0 && thread->errorStringSize == 0);
  PR_ASSERT(thread->errorStringLength == 0);
  PR_ASSERT(thread->name == 0);

  /* Reset data members in thread structure */
  thread->errorCode = thread->osErrorCode = 0;
  thread->io_pending = thread->io_suspended = PR_FALSE;
  thread->environment = 0;
  PR_INIT_CLIST(&thread->lockList);
}

PRStatus _PR_RecycleThread(PRThread* thread) {
  if (_PR_IS_NATIVE_THREAD(thread) && _PR_NUM_DEADNATIVE < _pr_recycleThreads) {
    _PR_DEADQ_LOCK;
    PR_APPEND_LINK(&thread->links, &_PR_DEADNATIVEQ);
    _PR_INC_DEADNATIVE;
    _PR_DEADQ_UNLOCK;
    return (PR_SUCCESS);
  } else if (!_PR_IS_NATIVE_THREAD(thread) &&
             _PR_NUM_DEADUSER < _pr_recycleThreads) {
    _PR_DEADQ_LOCK;
    PR_APPEND_LINK(&thread->links, &_PR_DEADUSERQ);
    _PR_INC_DEADUSER;
    _PR_DEADQ_UNLOCK;
    return (PR_SUCCESS);
  }
  return (PR_FAILURE);
}

/*
 * Decrement the active thread count, either _pr_systemActive or
 * _pr_userActive, depending on whether the thread is a system thread
 * or a user thread.  If all the user threads, except possibly
 * the primordial thread, have terminated, we notify the primordial
 * thread of this condition.
 *
 * Since this function will lock _pr_activeLock, do not call this
 * function while holding the _pr_activeLock lock, as this will result
 * in a deadlock.
 */


static void _PR_DecrActiveThreadCount(PRThread* thread) {
  PR_Lock(_pr_activeLock);
  if (thread->flags & _PR_SYSTEM) {
    _pr_systemActive--;
  } else {
    _pr_userActive--;
    if (_pr_userActive == _pr_primordialExitCount) {
      PR_NotifyCondVar(_pr_primordialExitCVar);
    }
  }
  PR_Unlock(_pr_activeLock);
}

/*
** Detach thread structure
*/

static void _PR_DestroyThread(PRThread* thread) {
  _PR_MD_FREE_LOCK(&thread->threadLock);
  PR_DELETE(thread);
}

void _PR_NativeDestroyThread(PRThread* thread) {
  if (thread->term) {
    PR_DestroyCondVar(thread->term);
    thread->term = 0;
  }
  if (NULL != thread->privateData) {
    PR_ASSERT(0 != thread->tpdLength);
    PR_DELETE(thread->privateData);
    thread->tpdLength = 0;
  }
  PR_DELETE(thread->stack);
  _PR_DestroyThread(thread);
}

void _PR_UserDestroyThread(PRThread* thread) {
  if (thread->term) {
    PR_DestroyCondVar(thread->term);
    thread->term = 0;
  }
  if (NULL != thread->privateData) {
    PR_ASSERT(0 != thread->tpdLength);
    PR_DELETE(thread->privateData);
    thread->tpdLength = 0;
  }
  _PR_MD_FREE_LOCK(&thread->threadLock);
  if (thread->threadAllocatedOnStack == 1) {
    _PR_MD_CLEAN_THREAD(thread);
    /*
     *  Because the no_sched field is set, this thread/stack will
     *  will not be re-used until the flag is cleared by the thread
     *  we will context switch to.
     */

    _PR_FreeStack(thread->stack);
  } else {
#ifdef WINNT
    _PR_MD_CLEAN_THREAD(thread);
#else
    /*
     * This assertion does not apply to NT.  On NT, every fiber
     * has its threadAllocatedOnStack equal to 0.  Elsewhere,
     * only the primordial thread has its threadAllocatedOnStack
     * equal to 0.
     */

    PR_ASSERT(thread->flags & _PR_PRIMORDIAL);
#endif
  }
}

/*
** Run a thread's start function. When the start function returns the
** thread is done executing and no longer needs the CPU. If there are no
** more user threads running then we can exit the program.
*/

void _PR_NativeRunThread(void* arg) {
  PRThread* thread = (PRThread*)arg;

  _PR_MD_SET_CURRENT_THREAD(thread);

  _PR_MD_SET_CURRENT_CPU(NULL);

  /* Set up the thread stack information */
  _PR_InitializeNativeStack(thread->stack);

  /* Set up the thread md information */
  if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
    /*
     * thread failed to initialize itself, possibly due to
     * failure to allocate per-thread resources
     */

    return;
  }

  while (1) {
    thread->state = _PR_RUNNING;

    /*
     * Add to list of active threads
     */

    PR_Lock(_pr_activeLock);
    PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ());
    _pr_global_threads++;
    PR_Unlock(_pr_activeLock);

    (*thread->startFunc)(thread->arg);

    /*
     * The following two assertions are meant for NT asynch io.
     *
     * The thread should have no asynch io in progress when it
     * exits, otherwise the overlapped buffer, which is part of
     * the thread structure, would become invalid.
     */

    PR_ASSERT(thread->io_pending == PR_FALSE);
    /*
     * This assertion enforces the programming guideline that
     * if an io function times out or is interrupted, the thread
     * should close the fd to force the asynch io to abort
     * before it exits.  Right now, closing the fd is the only
     * way to clear the io_suspended flag.
     */

    PR_ASSERT(thread->io_suspended == PR_FALSE);

    /*
     * remove thread from list of active threads
     */

    PR_Lock(_pr_activeLock);
    PR_REMOVE_LINK(&thread->active);
    _pr_global_threads--;
    PR_Unlock(_pr_activeLock);

    PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting"));

    /* All done, time to go away */
    _PR_CleanupThread(thread);

    _PR_NotifyJoinWaiters(thread);

    _PR_DecrActiveThreadCount(thread);

    thread->state = _PR_DEAD_STATE;

    if (!_pr_recycleThreads || (_PR_RecycleThread(thread) == PR_FAILURE)) {
      /*
       * thread not recycled
       * platform-specific thread exit processing
       *        - for stuff like releasing native-thread resources, etc.
       */

      _PR_MD_EXIT_THREAD(thread);
      /*
       * Free memory allocated for the thread
       */

      _PR_NativeDestroyThread(thread);
      /*
       * thread gone, cannot de-reference thread now
       */

      return;
    }

    /* Now wait for someone to activate us again... */
    _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT);
  }
}

static void _PR_UserRunThread(void) {
  PRThread* thread = _PR_MD_CURRENT_THREAD();
  PRIntn is;

  if (_MD_LAST_THREAD()) {
    _MD_LAST_THREAD()->no_sched = 0;
  }

#ifdef HAVE_CUSTOM_USER_THREADS
  if (thread->stack == NULL) {
    thread->stack = PR_NEWZAP(PRThreadStack);
    _PR_InitializeNativeStack(thread->stack);
  }
#endif /* HAVE_CUSTOM_USER_THREADS */

  while (1) {
    /* Run thread main */
    if (!_PR_IS_NATIVE_THREAD(thread)) {
      _PR_MD_SET_INTSOFF(0);
    }

    /*
     * Add to list of active threads
     */

    if (!(thread->flags & _PR_IDLE_THREAD)) {
      PR_Lock(_pr_activeLock);
      PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ());
      _pr_local_threads++;
      PR_Unlock(_pr_activeLock);
    }

    (*thread->startFunc)(thread->arg);

    /*
     * The following two assertions are meant for NT asynch io.
     *
     * The thread should have no asynch io in progress when it
     * exits, otherwise the overlapped buffer, which is part of
     * the thread structure, would become invalid.
     */

    PR_ASSERT(thread->io_pending == PR_FALSE);
    /*
     * This assertion enforces the programming guideline that
     * if an io function times out or is interrupted, the thread
     * should close the fd to force the asynch io to abort
     * before it exits.  Right now, closing the fd is the only
     * way to clear the io_suspended flag.
     */

    PR_ASSERT(thread->io_suspended == PR_FALSE);

    PR_Lock(_pr_activeLock);
    /*
     * remove thread from list of active threads
     */

    if (!(thread->flags & _PR_IDLE_THREAD)) {
      PR_REMOVE_LINK(&thread->active);
      _pr_local_threads--;
    }
    PR_Unlock(_pr_activeLock);
    PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting"));

    /* All done, time to go away */
    _PR_CleanupThread(thread);

    _PR_INTSOFF(is);

    _PR_NotifyJoinWaiters(thread);

    _PR_DecrActiveThreadCount(thread);

    thread->state = _PR_DEAD_STATE;

    if (!_pr_recycleThreads || (_PR_RecycleThread(thread) == PR_FAILURE)) {
      /*
      ** Destroy the thread resources
      */

      _PR_UserDestroyThread(thread);
    }

    /*
    ** Find another user thread to run. This cpu has finished the
    ** previous threads main and is now ready to run another thread.
    */

    {
      PRInt32 is;
      _PR_INTSOFF(is);
      _PR_MD_SWITCH_CONTEXT(thread);
    }

    /* Will land here when we get scheduled again if we are recycling... */
  }
}

void _PR_SetThreadPriority(PRThread* thread, PRThreadPriority newPri) {
  PRThread* me = _PR_MD_CURRENT_THREAD();
  PRIntn is;

  if (_PR_IS_NATIVE_THREAD(thread)) {
    _PR_MD_SET_PRIORITY(&(thread->md), newPri);
    return;
  }

  if (!_PR_IS_NATIVE_THREAD(me)) {
    _PR_INTSOFF(is);
  }
  _PR_THREAD_LOCK(thread);
  if (newPri != thread->priority) {
    _PRCPU* cpu = thread->cpu;

    switch (thread->state) {
      case _PR_RUNNING:
        /* Change my priority */

        _PR_RUNQ_LOCK(cpu);
        thread->priority = newPri;
        if (_PR_RUNQREADYMASK(cpu) >> (newPri + 1)) {
          if (!_PR_IS_NATIVE_THREAD(me)) {
            _PR_SET_RESCHED_FLAG();
          }
        }
        _PR_RUNQ_UNLOCK(cpu);
        break;

      case _PR_RUNNABLE:

        _PR_RUNQ_LOCK(cpu);
        /* Move to different runQ */
        _PR_DEL_RUNQ(thread);
        thread->priority = newPri;
        PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
        _PR_ADD_RUNQ(thread, cpu, newPri);
        _PR_RUNQ_UNLOCK(cpu);

        if (newPri > me->priority) {
          if (!_PR_IS_NATIVE_THREAD(me)) {
            _PR_SET_RESCHED_FLAG();
          }
        }

        break;

      case _PR_LOCK_WAIT:
      case _PR_COND_WAIT:
      case _PR_IO_WAIT:
      case _PR_SUSPENDED:

        thread->priority = newPri;
        break;
    }
  }
  _PR_THREAD_UNLOCK(thread);
  if (!_PR_IS_NATIVE_THREAD(me)) {
    _PR_INTSON(is);
  }
}

/*
** Suspend the named thread and copy its gc registers into regBuf
*/

static void _PR_Suspend(PRThread* thread) {
  PRIntn is;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  PR_ASSERT(thread != me);
  PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread) || (!thread->cpu));

  if (!_PR_IS_NATIVE_THREAD(me)) {
    _PR_INTSOFF(is);
  }
  _PR_THREAD_LOCK(thread);
  switch (thread->state) {
    case _PR_RUNNABLE:
      if (!_PR_IS_NATIVE_THREAD(thread)) {
        _PR_RUNQ_LOCK(thread->cpu);
        _PR_DEL_RUNQ(thread);
        _PR_RUNQ_UNLOCK(thread->cpu);

        _PR_MISCQ_LOCK(thread->cpu);
        _PR_ADD_SUSPENDQ(thread, thread->cpu);
        _PR_MISCQ_UNLOCK(thread->cpu);
      } else {
        /*
         * Only LOCAL threads are suspended by _PR_Suspend
         */

        PR_ASSERT(0);
      }
      thread->state = _PR_SUSPENDED;
      break;

    case _PR_RUNNING:
      /*
       * The thread being suspended should be a LOCAL thread with
       * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state
       */

      PR_ASSERT(0);
      break;

    case _PR_LOCK_WAIT:
    case _PR_IO_WAIT:
    case _PR_COND_WAIT:
      if (_PR_IS_NATIVE_THREAD(thread)) {
        _PR_MD_SUSPEND_THREAD(thread);
      }
      thread->flags |= _PR_SUSPENDING;
      break;

    default:
      PR_Abort();
  }
  _PR_THREAD_UNLOCK(thread);
  if (!_PR_IS_NATIVE_THREAD(me)) {
    _PR_INTSON(is);
  }
}

static void _PR_Resume(PRThread* thread) {
  PRThreadPriority pri;
  PRIntn is;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  if (!_PR_IS_NATIVE_THREAD(me)) {
    _PR_INTSOFF(is);
  }
  _PR_THREAD_LOCK(thread);
  switch (thread->state) {
    case _PR_SUSPENDED:
      thread->state = _PR_RUNNABLE;
      thread->flags &= ~_PR_SUSPENDING;
      if (!_PR_IS_NATIVE_THREAD(thread)) {
        _PR_MISCQ_LOCK(thread->cpu);
        _PR_DEL_SUSPENDQ(thread);
        _PR_MISCQ_UNLOCK(thread->cpu);

        pri = thread->priority;

        _PR_RUNQ_LOCK(thread->cpu);
        _PR_ADD_RUNQ(thread, thread->cpu, pri);
        _PR_RUNQ_UNLOCK(thread->cpu);

        if (pri > _PR_MD_CURRENT_THREAD()->priority) {
          if (!_PR_IS_NATIVE_THREAD(me)) {
            _PR_SET_RESCHED_FLAG();
          }
        }
      } else {
        PR_ASSERT(0);
      }
      break;

    case _PR_IO_WAIT:
    case _PR_COND_WAIT:
      thread->flags &= ~_PR_SUSPENDING;
      /*      PR_ASSERT(thread->wait.monitor->stickyCount == 0); */
      break;

    case _PR_LOCK_WAIT: {
      PRLock* wLock = thread->wait.lock;

      thread->flags &= ~_PR_SUSPENDING;

      _PR_LOCK_LOCK(wLock);
      if (thread->wait.lock->owner == 0) {
        _PR_UnblockLockWaiter(thread->wait.lock);
      }
      _PR_LOCK_UNLOCK(wLock);
      break;
    }
    case _PR_RUNNABLE:
      break;
    case _PR_RUNNING:
      /*
       * The thread being suspended should be a LOCAL thread with
       * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state
       */

      PR_ASSERT(0);
      break;

    default:
      /*
       * thread should have been in one of the above-listed blocked states
       * (_PR_JOIN_WAIT, _PR_IO_WAIT, _PR_UNBORN, _PR_DEAD_STATE)
       */

      PR_Abort();
  }
  _PR_THREAD_UNLOCK(thread);
  if (!_PR_IS_NATIVE_THREAD(me)) {
    _PR_INTSON(is);
  }
}

#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
static PRThread* get_thread(_PRCPU* cpu, PRBool* wakeup_cpus) {
  PRThread* thread;
  PRIntn pri;
  PRUint32 r;
  PRCList* qp;
  PRIntn priMin, priMax;

  _PR_RUNQ_LOCK(cpu);
  r = _PR_RUNQREADYMASK(cpu);
  if (r == 0) {
    priMin = priMax = PR_PRIORITY_FIRST;
  } else if (r == (1 << PR_PRIORITY_NORMAL)) {
    priMin = priMax = PR_PRIORITY_NORMAL;
  } else {
    priMin = PR_PRIORITY_FIRST;
    priMax = PR_PRIORITY_LAST;
  }
  thread = NULL;
  for (pri = priMax; pri >= priMin; pri--) {
    if (r & (1 << pri)) {
      for (qp = _PR_RUNQ(cpu)[pri].next; qp != &_PR_RUNQ(cpu)[pri];
           qp = qp->next) {
        thread = _PR_THREAD_PTR(qp);
        /*
         * skip non-schedulable threads
         */

        PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
        if (thread->no_sched) {
          thread = NULL;
          /*
           * Need to wakeup cpus to avoid missing a
           * runnable thread
           * Waking up all CPU's need happen only once.
           */


          *wakeup_cpus = PR_TRUE;
          continue;
        } else if (thread->flags & _PR_BOUND_THREAD) {
          /*
           * Thread bound to cpu 0
           */


          thread = NULL;
          continue;
        } else if (thread->io_pending == PR_TRUE) {
          /*
           * A thread that is blocked for I/O needs to run
           * on the same cpu on which it was blocked. This is because
           * the cpu's ioq is accessed without lock protection and scheduling
           * the thread on a different cpu would preclude this optimization.
           */

          thread = NULL;
          continue;
        } else {
          /* Pull thread off of its run queue */
          _PR_DEL_RUNQ(thread);
          _PR_RUNQ_UNLOCK(cpu);
          return (thread);
        }
      }
    }
    thread = NULL;
  }
  _PR_RUNQ_UNLOCK(cpu);
  return (thread);
}
#endif /* !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) */

/*
** Schedule this native thread by finding the highest priority nspr
** thread that is ready to run.
**
** Note- everyone really needs to call _PR_MD_SWITCH_CONTEXT (which calls
**       PR_Schedule() rather than calling PR_Schedule.  Otherwise if there
**       is initialization required for switching from SWITCH_CONTEXT,
**       it will not get done!
*/

void _PR_Schedule(void) {
  PRThread *thread, *me = _PR_MD_CURRENT_THREAD();
  _PRCPU* cpu = _PR_MD_CURRENT_CPU();
  PRIntn pri;
  PRUint32 r;
  PRCList* qp;
  PRIntn priMin, priMax;
#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
  PRBool wakeup_cpus;
#endif

  /* Interrupts must be disabled */
  PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);

  /* Since we are rescheduling, we no longer want to */
  _PR_CLEAR_RESCHED_FLAG();

  /*
  ** Find highest priority thread to run. Bigger priority numbers are
  ** higher priority threads
  */

  _PR_RUNQ_LOCK(cpu);
  /*
   *  if we are in SuspendAll mode, can schedule only the thread
   *    that called PR_SuspendAll
   *
   *  The thread may be ready to run now, after completing an I/O
   *  operation, for example
   */

  if ((thread = suspendAllThread) != 0) {
    if ((!(thread->no_sched)) && (thread->state == _PR_RUNNABLE)) {
      /* Pull thread off of its run queue */
      _PR_DEL_RUNQ(thread);
      _PR_RUNQ_UNLOCK(cpu);
      goto found_thread;
    } else {
      thread = NULL;
      _PR_RUNQ_UNLOCK(cpu);
      goto idle_thread;
    }
  }
  r = _PR_RUNQREADYMASK(cpu);
  if (r == 0) {
    priMin = priMax = PR_PRIORITY_FIRST;
  } else if (r == (1 << PR_PRIORITY_NORMAL)) {
    priMin = priMax = PR_PRIORITY_NORMAL;
  } else {
    priMin = PR_PRIORITY_FIRST;
    priMax = PR_PRIORITY_LAST;
  }
  thread = NULL;
  for (pri = priMax; pri >= priMin; pri--) {
    if (r & (1 << pri)) {
      for (qp = _PR_RUNQ(cpu)[pri].next; qp != &_PR_RUNQ(cpu)[pri];
           qp = qp->next) {
        thread = _PR_THREAD_PTR(qp);
        /*
         * skip non-schedulable threads
         */

        PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
        if ((thread->no_sched) && (me != thread)) {
          thread = NULL;
          continue;
        } else {
          /* Pull thread off of its run queue */
          _PR_DEL_RUNQ(thread);
          _PR_RUNQ_UNLOCK(cpu);
          goto found_thread;
        }
      }
    }
    thread = NULL;
  }
  _PR_RUNQ_UNLOCK(cpu);

#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)

  wakeup_cpus = PR_FALSE;
  _PR_CPU_LIST_LOCK();
  for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) {
    if (cpu != _PR_CPU_PTR(qp)) {
      if ((thread = get_thread(_PR_CPU_PTR(qp), &wakeup_cpus)) != NULL) {
        thread->cpu = cpu;
        _PR_CPU_LIST_UNLOCK();
        if (wakeup_cpus == PR_TRUE) {
          _PR_MD_WAKEUP_CPUS();
        }
        goto found_thread;
      }
    }
  }
  _PR_CPU_LIST_UNLOCK();
  if (wakeup_cpus == PR_TRUE) {
    _PR_MD_WAKEUP_CPUS();
  }

#endif /* _PR_LOCAL_THREADS_ONLY */

idle_thread:
  /*
   ** There are no threads to run. Switch to the idle thread
   */

  PR_LOG(_pr_sched_lm, PR_LOG_MAX, ("pausing"));
  thread = _PR_MD_CURRENT_CPU()->idle_thread;

found_thread:
  PR_ASSERT((me == thread) ||
            ((thread->state == _PR_RUNNABLE) && (!(thread->no_sched))));

  /* Resume the thread */
  PR_LOG(_pr_sched_lm, PR_LOG_MAX, ("switching to %d[%p]", thread->id, thread));
  PR_ASSERT(thread->state != _PR_RUNNING);
  thread->state = _PR_RUNNING;

  /* If we are on the runq, it just means that we went to sleep on some
   * resource, and by the time we got here another real native thread had
   * already given us the resource and put us back on the runqueue
   */

  PR_ASSERT(thread->cpu == _PR_MD_CURRENT_CPU());
  if (thread != me) {
    _PR_MD_RESTORE_CONTEXT(thread);
  }
#if 0
    /* XXXMB; with setjmp/longjmp it is impossible to land here, but
     * it is not with fibers... Is this a bad thing?  I believe it is
     * still safe.
     */

    PR_NOT_REACHED("impossible return from schedule");
#endif
}

/*
** Attaches a thread.
** Does not set the _PR_MD_CURRENT_THREAD.
** Does not specify the scope of the thread.
*/

static PRThread* _PR_AttachThread(PRThreadType type, PRThreadPriority priority,
                                  PRThreadStack* stack) {
  PRThread* thread;
  char* mem;

  if (priority > PR_PRIORITY_LAST) {
    priority = PR_PRIORITY_LAST;
  } else if (priority < PR_PRIORITY_FIRST) {
    priority = PR_PRIORITY_FIRST;
  }

  mem = (char*)PR_CALLOC(sizeof(PRThread));
  if (mem) {
    thread = (PRThread*)mem;
    thread->priority = priority;
    thread->stack = stack;
    thread->state = _PR_RUNNING;
    PR_INIT_CLIST(&thread->lockList);
    if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) {
      PR_DELETE(thread);
      return 0;
    }

    return thread;
  }
  return 0;
}

PR_IMPLEMENT(PRThread*)
_PR_NativeCreateThread(PRThreadType type, void (*start)(void* arg), void* arg,
                       PRThreadPriority priority, PRThreadScope scope,
                       PRThreadState state, PRUint32 stackSize,
                       PRUint32 flags) {
  PRThread* thread;

  thread = _PR_AttachThread(type, priority, NULL);

  if (thread) {
    PR_Lock(_pr_activeLock);
    thread->flags = (flags | _PR_GLOBAL_SCOPE);
    thread->id = ++_pr_utid;
    if (type == PR_SYSTEM_THREAD) {
      thread->flags |= _PR_SYSTEM;
      _pr_systemActive++;
    } else {
      _pr_userActive++;
    }
    PR_Unlock(_pr_activeLock);

    thread->stack = PR_NEWZAP(PRThreadStack);
    if (!thread->stack) {
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
      goto done;
    }
    thread->stack->stackSize = stackSize ? stackSize : _MD_DEFAULT_STACK_SIZE;
    thread->stack->thr = thread;
    thread->startFunc = start;
    thread->arg = arg;

    /*
      Set thread flags related to scope and joinable state. If joinable
      thread, allocate a "termination" conidition variable.
     */

    if (state == PR_JOINABLE_THREAD) {
      thread->term = PR_NewCondVar(_pr_terminationCVLock);
      if (thread->term == NULL) {
        PR_DELETE(thread->stack);
        goto done;
      }
    }

    thread->state = _PR_RUNNING;
    if (_PR_MD_CREATE_THREAD(thread, _PR_NativeRunThread, priority, scope,
                             state, stackSize) == PR_SUCCESS) {
      return thread;
    }
    if (thread->term) {
      PR_DestroyCondVar(thread->term);
      thread->term = NULL;
    }
    PR_DELETE(thread->stack);
  }

done:
  if (thread) {
    _PR_DecrActiveThreadCount(thread);
    _PR_DestroyThread(thread);
  }
  return NULL;
}

/************************************************************************/

PR_IMPLEMENT(PRThread*)
_PR_CreateThread(PRThreadType type, void (*start)(void* arg), void* arg,
                 PRThreadPriority priority, PRThreadScope scope,
                 PRThreadState state, PRUint32 stackSize, PRUint32 flags) {
  PRThread* me;
  PRThread* thread = NULL;
  PRThreadStack* stack;
  char* top;
  PRIntn is;
  PRIntn native = 0;
  PRIntn useRecycled = 0;
  PRBool status;

  /*
  First, pin down the priority.  Not all compilers catch passing out of
  range enum here.  If we let bad values thru, priority queues won't work.
  */

  if (priority > PR_PRIORITY_LAST) {
    priority = PR_PRIORITY_LAST;
  } else if (priority < PR_PRIORITY_FIRST) {
    priority = PR_PRIORITY_FIRST;
  }

  if (!_pr_initialized) {
    _PR_ImplicitInitialization();
  }

  if (!(flags & _PR_IDLE_THREAD)) {
    me = _PR_MD_CURRENT_THREAD();
  }

#if defined(_PR_GLOBAL_THREADS_ONLY)
  /*
   * can create global threads only
   */

  if (scope == PR_LOCAL_THREAD) {
    scope = PR_GLOBAL_THREAD;
  }
#endif

  if (_native_threads_only) {
    scope = PR_GLOBAL_THREAD;
  }

  native =
      (((scope == PR_GLOBAL_THREAD) || (scope == PR_GLOBAL_BOUND_THREAD)) &&
       _PR_IS_NATIVE_THREAD_SUPPORTED());

  _PR_ADJUST_STACKSIZE(stackSize);

  if (native) {
    /*
     * clear the IDLE_THREAD flag which applies to LOCAL
     * threads only
     */

    flags &= ~_PR_IDLE_THREAD;
    flags |= _PR_GLOBAL_SCOPE;
    if (_PR_NUM_DEADNATIVE > 0) {
      _PR_DEADQ_LOCK;

      if (_PR_NUM_DEADNATIVE == 0) { /* Thread safe check */
        _PR_DEADQ_UNLOCK;
      } else {
        thread = _PR_THREAD_PTR(_PR_DEADNATIVEQ.next);
        PR_REMOVE_LINK(&thread->links);
        _PR_DEC_DEADNATIVE;
        _PR_DEADQ_UNLOCK;

        _PR_InitializeRecycledThread(thread);
        thread->startFunc = start;
        thread->arg = arg;
        thread->flags = (flags | _PR_GLOBAL_SCOPE);
        if (type == PR_SYSTEM_THREAD) {
          thread->flags |= _PR_SYSTEM;
          PR_ATOMIC_INCREMENT(&_pr_systemActive);
        } else {
          PR_ATOMIC_INCREMENT(&_pr_userActive);
        }

        if (state == PR_JOINABLE_THREAD) {
          if (!thread->term) {
            thread->term = PR_NewCondVar(_pr_terminationCVLock);
          }
        } else {
          if (thread->term) {
            PR_DestroyCondVar(thread->term);
            thread->term = 0;
          }
        }

        thread->priority = priority;
        _PR_MD_SET_PRIORITY(&(thread->md), priority);
        /* XXX what about stackSize? */
        thread->state = _PR_RUNNING;
        _PR_MD_WAKEUP_WAITER(thread);
        return thread;
      }
    }
    thread = _PR_NativeCreateThread(type, start, arg, priority, scope, state,
                                    stackSize, flags);
  } else {
    if (_PR_NUM_DEADUSER > 0) {
      _PR_DEADQ_LOCK;

      if (_PR_NUM_DEADUSER == 0) { /* thread safe check */
        _PR_DEADQ_UNLOCK;
      } else {
        PRCList* ptr;

        /* Go down list checking for a recycled thread with a
         * large enough stack.  XXXMB - this has a bad degenerate case.
         */

        ptr = _PR_DEADUSERQ.next;
        while (ptr != &_PR_DEADUSERQ) {
          thread = _PR_THREAD_PTR(ptr);
          if ((thread->stack->stackSize >= stackSize) && (!thread->no_sched)) {
            PR_REMOVE_LINK(&thread->links);
            _PR_DEC_DEADUSER;
            break;
          } else {
            ptr = ptr->next;
            thread = NULL;
          }
        }

        _PR_DEADQ_UNLOCK;

        if (thread) {
          _PR_InitializeRecycledThread(thread);
          thread->startFunc = start;
          thread->arg = arg;
          thread->priority = priority;
          if (state == PR_JOINABLE_THREAD) {
            if (!thread->term) {
              thread->term = PR_NewCondVar(_pr_terminationCVLock);
            }
          } else {
            if (thread->term) {
              PR_DestroyCondVar(thread->term);
              thread->term = 0;
            }
          }
          useRecycled++;
        }
      }
    }
    if (thread == NULL) {
#ifndef HAVE_CUSTOM_USER_THREADS
      stack = _PR_NewStack(stackSize);
      if (!stack) {
        PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
        return NULL;
      }

      /* Allocate thread object and per-thread data off the top of the stack*/
      top = stack->stackTop;
#  ifdef HAVE_STACK_GROWING_UP
      thread = (PRThread*)top;
      top = top + sizeof(PRThread);
      /*
       * Make stack 64-byte aligned
       */

      if ((PRUptrdiff)top & 0x3f) {
        top = (char*)(((PRUptrdiff)top + 0x40) & ~0x3f);
      }
#  else
      top = top - sizeof(PRThread);
      thread = (PRThread*)top;
      /*
       * Make stack 64-byte aligned
       */

      if ((PRUptrdiff)top & 0x3f) {
        top = (char*)((PRUptrdiff)top & ~0x3f);
      }
#  endif
      stack->thr = thread;
      memset(thread, 0, sizeof(PRThread));
      thread->threadAllocatedOnStack = 1;
#else
      thread = _PR_MD_CREATE_USER_THREAD(stackSize, start, arg);
      if (!thread) {
        PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
        return NULL;
      }
      thread->threadAllocatedOnStack = 0;
      stack = NULL;
      top = NULL;
#endif

      /* Initialize thread */
      thread->tpdLength = 0;
      thread->privateData = NULL;
      thread->stack = stack;
      thread->priority = priority;
      thread->startFunc = start;
      thread->arg = arg;
      PR_INIT_CLIST(&thread->lockList);

      if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
        if (thread->threadAllocatedOnStack == 1) {
          _PR_FreeStack(thread->stack);
        } else {
          PR_DELETE(thread);
        }
        PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
        return NULL;
      }

      if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) {
        if (thread->threadAllocatedOnStack == 1) {
          _PR_FreeStack(thread->stack);
        } else {
          PR_DELETE(thread->privateData);
          PR_DELETE(thread);
        }
        PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
        return NULL;
      }

      _PR_MD_INIT_CONTEXT(thread, top, _PR_UserRunThread, &status);

      if (status == PR_FALSE) {
        _PR_MD_FREE_LOCK(&thread->threadLock);
        if (thread->threadAllocatedOnStack == 1) {
          _PR_FreeStack(thread->stack);
        } else {
          PR_DELETE(thread->privateData);
          PR_DELETE(thread);
        }
        return NULL;
      }

      /*
        Set thread flags related to scope and joinable state. If joinable
        thread, allocate a "termination" condition variable.
      */

      if (state == PR_JOINABLE_THREAD) {
        thread->term = PR_NewCondVar(_pr_terminationCVLock);
        if (thread->term == NULL) {
          _PR_MD_FREE_LOCK(&thread->threadLock);
          if (thread->threadAllocatedOnStack == 1) {
            _PR_FreeStack(thread->stack);
          } else {
            PR_DELETE(thread->privateData);
            PR_DELETE(thread);
          }
          return NULL;
        }
      }
    }

    /* Update thread type counter */
    PR_Lock(_pr_activeLock);
    thread->flags = flags;
    thread->id = ++_pr_utid;
    if (type == PR_SYSTEM_THREAD) {
      thread->flags |= _PR_SYSTEM;
      _pr_systemActive++;
    } else {
      _pr_userActive++;
    }

    /* Make thread runnable */
    thread->state = _PR_RUNNABLE;
    /*
     * Add to list of active threads
     */

    PR_Unlock(_pr_activeLock);

    if ((!(thread->flags & _PR_IDLE_THREAD)) && _PR_IS_NATIVE_THREAD(me)) {
      thread->cpu = _PR_GetPrimordialCPU();
    } else {
      thread->cpu = _PR_MD_CURRENT_CPU();
    }

    PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));

    if ((!(thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me)) {
      _PR_INTSOFF(is);
      _PR_RUNQ_LOCK(thread->cpu);
      _PR_ADD_RUNQ(thread, thread->cpu, priority);
      _PR_RUNQ_UNLOCK(thread->cpu);
    }

    if (thread->flags & _PR_IDLE_THREAD) {
      /*
      ** If the creating thread is a kernel thread, we need to
      ** awaken the user thread idle thread somehow; potentially
      ** it could be sleeping in its idle loop, and we need to poke
      ** it.  To do so, wake the idle thread...
      */

      _PR_MD_WAKEUP_WAITER(NULL);
    } else if (_PR_IS_NATIVE_THREAD(me)) {
      _PR_MD_WAKEUP_WAITER(thread);
    }
    if ((!(thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me)) {
      _PR_INTSON(is);
    }
  }

  return thread;
}

PR_IMPLEMENT(PRThread*)
PR_CreateThread(PRThreadType type, void (*start)(void* arg), void* arg,
                PRThreadPriority priority, PRThreadScope scope,
                PRThreadState state, PRUint32 stackSize) {
  return _PR_CreateThread(type, start, arg, priority, scope, state, stackSize,
                          0);
}

/*
** Associate a thread object with an existing native thread.
**     "type" is the type of thread object to attach
**     "priority" is the priority to assign to the thread
**     "stack" defines the shape of the threads stack
**
** This can return NULL if some kind of error occurs, or if memory is
** tight.
**
** This call is not normally needed unless you create your own native
** thread. PR_Init does this automatically for the primordial thread.
*/

PRThread* _PRI_AttachThread(PRThreadType type, PRThreadPriority priority,
                            PRThreadStack* stack, PRUint32 flags) {
  PRThread* thread;

  if ((thread = _PR_MD_GET_ATTACHED_THREAD()) != NULL) {
    return thread;
  }
  _PR_MD_SET_CURRENT_THREAD(NULL);

  /* Clear out any state if this thread was attached before */
  _PR_MD_SET_CURRENT_CPU(NULL);

  thread = _PR_AttachThread(type, priority, stack);
  if (thread) {
    PRIntn is;

    _PR_MD_SET_CURRENT_THREAD(thread);

    thread->flags = flags | _PR_GLOBAL_SCOPE | _PR_ATTACHED;

    if (!stack) {
      thread->stack = PR_NEWZAP(PRThreadStack);
      if (!thread->stack) {
        _PR_DestroyThread(thread);
        return NULL;
      }
      thread->stack->stackSize = _MD_DEFAULT_STACK_SIZE;
    }
    PR_INIT_CLIST(&thread->links);

    if (_PR_MD_INIT_ATTACHED_THREAD(thread) == PR_FAILURE) {
      PR_DELETE(thread->stack);
      _PR_DestroyThread(thread);
      return NULL;
    }

    _PR_MD_SET_CURRENT_CPU(NULL);

    if (_PR_MD_CURRENT_CPU()) {
      _PR_INTSOFF(is);
      PR_Lock(_pr_activeLock);
    }
    if (type == PR_SYSTEM_THREAD) {
      thread->flags |= _PR_SYSTEM;
      _pr_systemActive++;
    } else {
      _pr_userActive++;
    }
    if (_PR_MD_CURRENT_CPU()) {
      PR_Unlock(_pr_activeLock);
      _PR_INTSON(is);
    }
  }
  return thread;
}

PR_IMPLEMENT(PRThread*)
PR_AttachThread(PRThreadType type, PRThreadPriority priority,
                PRThreadStack* stack) {
  return PR_GetCurrentThread();
}

PR_IMPLEMENT(void) PR_DetachThread(void) {
  /*
   * On Solaris, and Windows, foreign threads are detached when
   * they terminate.
   */

#if !defined(WIN32) && !(defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY))
  PRThread* me;
  if (_pr_initialized) {
    me = _PR_MD_GET_ATTACHED_THREAD();
    if ((me != NULL) && (me->flags & _PR_ATTACHED)) {
      _PRI_DetachThread();
    }
  }
#endif
}

void _PRI_DetachThread(void) {
  PRThread* me = _PR_MD_CURRENT_THREAD();

  if (me->flags & _PR_PRIMORDIAL) {
    /*
     * ignore, if primordial thread
     */

    return;
  }
  PR_ASSERT(me->flags & _PR_ATTACHED);
  PR_ASSERT(_PR_IS_NATIVE_THREAD(me));
  _PR_CleanupThread(me);
  PR_DELETE(me->privateData);

  _PR_DecrActiveThreadCount(me);

  _PR_MD_CLEAN_THREAD(me);
  _PR_MD_SET_CURRENT_THREAD(NULL);
  if (!me->threadAllocatedOnStack) {
    PR_DELETE(me->stack);
  }
  _PR_MD_FREE_LOCK(&me->threadLock);
  PR_DELETE(me);
}

/*
** Wait for thread termination:
**     "thread" is the target thread
**
** This can return PR_FAILURE if no joinable thread could be found
** corresponding to the specified target thread.
**
** The calling thread is suspended until the target thread completes.
** Several threads cannot wait for the same thread to complete; one thread
** will complete successfully and others will terminate with an error
*PR_FAILURE.
** The calling thread will not be blocked if the target thread has already
** terminated.
*/

PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread* thread) {
  PRIntn is;
  PRCondVar* term;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  if (!_PR_IS_NATIVE_THREAD(me)) {
    _PR_INTSOFF(is);
  }
  term = thread->term;
  /* can't join a non-joinable thread */
  if (term == NULL) {
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    goto ErrorExit;
  }

  /* multiple threads can't wait on the same joinable thread */
  if (term->condQ.next != &term->condQ) {
    goto ErrorExit;
  }
  if (!_PR_IS_NATIVE_THREAD(me)) {
    _PR_INTSON(is);
  }

  /* wait for the target thread's termination cv invariant */
  PR_Lock(_pr_terminationCVLock);
  while (thread->state != _PR_JOIN_WAIT) {
    (void)PR_WaitCondVar(term, PR_INTERVAL_NO_TIMEOUT);
  }
  (void)PR_Unlock(_pr_terminationCVLock);

  /*
   Remove target thread from global waiting to join Q; make it runnable
   again and put it back on its run Q.  When it gets scheduled later in
   _PR_RunThread code, it will clean up its stack.
  */

  if (!_PR_IS_NATIVE_THREAD(me)) {
    _PR_INTSOFF(is);
  }
  thread->state = _PR_RUNNABLE;
  if (!_PR_IS_NATIVE_THREAD(thread)) {
    _PR_THREAD_LOCK(thread);

    _PR_MISCQ_LOCK(thread->cpu);
    _PR_DEL_JOINQ(thread);
    _PR_MISCQ_UNLOCK(thread->cpu);

    _PR_AddThreadToRunQ(me, thread);
    _PR_THREAD_UNLOCK(thread);
  }
  if (!_PR_IS_NATIVE_THREAD(me)) {
    _PR_INTSON(is);
  }

  _PR_MD_WAKEUP_WAITER(thread);

  return PR_SUCCESS;

ErrorExit:
  if (!_PR_IS_NATIVE_THREAD(me)) {
    _PR_INTSON(is);
  }
  return PR_FAILURE;
}

PR_IMPLEMENT(void)
PR_SetThreadPriority(PRThread* thread, PRThreadPriority newPri) {
  /*
  First, pin down the priority.  Not all compilers catch passing out of
  range enum here.  If we let bad values thru, priority queues won't work.
  */

  if ((PRIntn)newPri > (PRIntn)PR_PRIORITY_LAST) {
    newPri = PR_PRIORITY_LAST;
  } else if ((PRIntn)newPri < (PRIntn)PR_PRIORITY_FIRST) {
    newPri = PR_PRIORITY_FIRST;
  }

  if (_PR_IS_NATIVE_THREAD(thread)) {
    thread->priority = newPri;
    _PR_MD_SET_PRIORITY(&(thread->md), newPri);
  } else {
    _PR_SetThreadPriority(thread, newPri);
  }
}

PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char* name) {
  PRThread* thread;
  size_t nameLen;

  if (!name) {
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    return PR_FAILURE;
  }

  thread = PR_GetCurrentThread();
  if (!thread) {
    return PR_FAILURE;
  }

  PR_Free(thread->name);
  nameLen = strlen(name);
  thread->name = (char*)PR_Malloc(nameLen + 1);
  if (!thread->name) {
    return PR_FAILURE;
  }
  memcpy(thread->name, name, nameLen + 1);
  _PR_MD_SET_CURRENT_THREAD_NAME(thread->name);
  return PR_SUCCESS;
}

PR_IMPLEMENT(const char*) PR_GetThreadName(const PRThread* thread) {
  if (!thread) {
    return NULL;
  }
  return thread->name;
}

/*
** This routine prevents all other threads from running. This call is needed by
** the garbage collector.
*/

PR_IMPLEMENT(void) PR_SuspendAll(void) {
  PRThread* me = _PR_MD_CURRENT_THREAD();
  PRCList* qp;

  /*
   * Stop all user and native threads which are marked GC able.
   */

  PR_Lock(_pr_activeLock);
  suspendAllOn = PR_TRUE;
  suspendAllThread = _PR_MD_CURRENT_THREAD();
  _PR_MD_BEGIN_SUSPEND_ALL();
  for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; qp != &_PR_ACTIVE_LOCAL_THREADQ();
       qp = qp->next) {
    if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
        _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) {
      _PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp));
      PR_ASSERT((_PR_ACTIVE_THREAD_PTR(qp))->state != _PR_RUNNING);
    }
  }
  for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
       qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) {
    if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
        _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp)))
    /* PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); */
    {
      _PR_MD_SUSPEND_THREAD(_PR_ACTIVE_THREAD_PTR(qp));
    }
  }
  _PR_MD_END_SUSPEND_ALL();
}

/*
** This routine unblocks all other threads that were suspended from running by
** PR_SuspendAll(). This call is needed by the garbage collector.
*/

PR_IMPLEMENT(void) PR_ResumeAll(void) {
  PRThread* me = _PR_MD_CURRENT_THREAD();
  PRCList* qp;

  /*
   * Resume all user and native threads which are marked GC able.
   */

  _PR_MD_BEGIN_RESUME_ALL();
  for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; qp != &_PR_ACTIVE_LOCAL_THREADQ();
       qp = qp->next) {
    if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
        _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) {
      _PR_Resume(_PR_ACTIVE_THREAD_PTR(qp));
    }
  }
  for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
       qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) {
    if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
        _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) {
      _PR_MD_RESUME_THREAD(_PR_ACTIVE_THREAD_PTR(qp));
    }
  }
  _PR_MD_END_RESUME_ALL();
  suspendAllThread = NULL;
  suspendAllOn = PR_FALSE;
  PR_Unlock(_pr_activeLock);
}

PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void* arg) {
  PRCList *qp, *qp_next;
  PRIntn i = 0;
  PRStatus rv = PR_SUCCESS;
  PRThread* t;

  /*
  ** Currently Enumerate threads happen only with suspension and
  ** pr_activeLock held
  */

  PR_ASSERT(suspendAllOn);

  /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking
   * qp->next after applying the function "func".  In particular, "func"
   * might remove the thread from the queue and put it into another one in
   * which case qp->next no longer points to the next entry in the original
   * queue.
   *
   * To get around this problem, we save qp->next in qp_next before applying
   * "func" and use that saved value as the next value after applying "func".
   */


  /*
   * Traverse the list of local and global threads
   */

  for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; qp != &_PR_ACTIVE_LOCAL_THREADQ();
       qp = qp_next) {
    qp_next = qp->next;
    t = _PR_ACTIVE_THREAD_PTR(qp);
    if (_PR_IS_GCABLE_THREAD(t)) {
      rv = (*func)(t, i, arg);
      if (rv != PR_SUCCESS) {
        return rv;
      }
      i++;
    }
  }
  for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
       qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp_next) {
    qp_next = qp->next;
    t = _PR_ACTIVE_THREAD_PTR(qp);
    if (_PR_IS_GCABLE_THREAD(t)) {
      rv = (*func)(t, i, arg);
      if (rv != PR_SUCCESS) {
        return rv;
      }
      i++;
    }
  }
  return rv;
}

/* FUNCTION: _PR_AddSleepQ
** DESCRIPTION:
**    Adds a thread to the sleep/pauseQ.
** RESTRICTIONS:
**    Caller must have the RUNQ lock.
**    Caller must be a user level thread
*/

PR_IMPLEMENT(void)
_PR_AddSleepQ(PRThread* thread, PRIntervalTime timeout) {
  _PRCPU* cpu = thread->cpu;

  if (timeout == PR_INTERVAL_NO_TIMEOUT) {
    /* append the thread to the global pause Q */
    PR_APPEND_LINK(&thread->links, &_PR_PAUSEQ(thread->cpu));
    thread->flags |= _PR_ON_PAUSEQ;
  } else {
    PRIntervalTime sleep;
    PRCList* q;
    PRThread* t;

    /* sort onto global sleepQ */
    sleep = timeout;

    /* Check if we are longest timeout */
    if (timeout >= _PR_SLEEPQMAX(cpu)) {
      PR_INSERT_BEFORE(&thread->links, &_PR_SLEEPQ(cpu));
      thread->sleep = timeout - _PR_SLEEPQMAX(cpu);
      _PR_SLEEPQMAX(cpu) = timeout;
    } else {
      /* Sort thread into global sleepQ at appropriate point */
      q = _PR_SLEEPQ(cpu).next;

      /* Now scan the list for where to insert this entry */
      while (q != &_PR_SLEEPQ(cpu)) {
        t = _PR_THREAD_PTR(q);
        if (sleep < t->sleep) {
          /* Found sleeper to insert in front of */
          break;
        }
        sleep -= t->sleep;
        q = q->next;
      }
      thread->sleep = sleep;
      PR_INSERT_BEFORE(&thread->links, q);

      /*
      ** Subtract our sleep time from the sleeper that follows us (there
      ** must be one) so that they remain relative to us.
      */

      PR_ASSERT(thread->links.next != &_PR_SLEEPQ(cpu));

      t = _PR_THREAD_PTR(thread->links.next);
      PR_ASSERT(_PR_THREAD_PTR(t->links.prev) == thread);
      t->sleep -= sleep;
    }

    thread->flags |= _PR_ON_SLEEPQ;
  }
}

/* FUNCTION: _PR_DelSleepQ
** DESCRIPTION:
**    Removes a thread from the sleep/pauseQ.
** INPUTS:
**    If propogate_time is true, then the thread following the deleted
**    thread will be get the time from the deleted thread.  This is used
**    when deleting a sleeper that has not timed out.
** RESTRICTIONS:
**    Caller must have the RUNQ lock.
**    Caller must be a user level thread
*/

PR_IMPLEMENT(void)
_PR_DelSleepQ(PRThread* thread, PRBool propogate_time) {
  _PRCPU* cpu = thread->cpu;

  /* Remove from pauseQ/sleepQ */
  if (thread->flags & (_PR_ON_PAUSEQ | _PR_ON_SLEEPQ)) {
    if (thread->flags & _PR_ON_SLEEPQ) {
      PRCList* q = thread->links.next;
      if (q != &_PR_SLEEPQ(cpu)) {
        if (propogate_time == PR_TRUE) {
          PRThread* after = _PR_THREAD_PTR(q);
          after->sleep += thread->sleep;
        } else {
          _PR_SLEEPQMAX(cpu) -= thread->sleep;
        }
      } else {
        /* Check if prev is the beggining of the list; if so,
         * we are the only element on the list.
         */

        if (thread->links.prev != &_PR_SLEEPQ(cpu)) {
          _PR_SLEEPQMAX(cpu) -= thread->sleep;
        } else {
          _PR_SLEEPQMAX(cpu) = 0;
        }
      }
      thread->flags &= ~_PR_ON_SLEEPQ;
    } else {
      thread->flags &= ~_PR_ON_PAUSEQ;
    }
    PR_REMOVE_LINK(&thread->links);
  } else {
    PR_ASSERT(0);
  }
}

void _PR_AddThreadToRunQ(
    PRThread* me,     /* the current thread */
    PRThread* thread) /* the local thread to be added to a run queue */
{
  PRThreadPriority pri = thread->priority;
  _PRCPU* cpu = thread->cpu;

  PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));

#if defined(WINNT)
  /*
   * On NT, we can only reliably know that the current CPU
   * is not idle.  We add the awakened thread to the run
   * queue of its CPU if its CPU is the current CPU.
   * For any other CPU, we don't really know whether it
   * is busy or idle.  So in all other cases, we just
   * "post" the awakened thread to the IO completion port
   * for the next idle CPU to execute (this is done in
   * _PR_MD_WAKEUP_WAITER).
   * Threads with a suspended I/O operation remain bound to
   * the same cpu until I/O is cancelled
   *
   * NOTE: the boolean expression below must be the exact
   * opposite of the corresponding boolean expression in
   * _PR_MD_WAKEUP_WAITER.
   */

  if ((!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) ||
      (thread->md.thr_bound_cpu)) {
    PR_ASSERT(!thread->md.thr_bound_cpu || (thread->md.thr_bound_cpu == cpu));
    _PR_RUNQ_LOCK(cpu);
    _PR_ADD_RUNQ(thread, cpu, pri);
    _PR_RUNQ_UNLOCK(cpu);
  }
#else
  _PR_RUNQ_LOCK(cpu);
  _PR_ADD_RUNQ(thread, cpu, pri);
  _PR_RUNQ_UNLOCK(cpu);
  if (!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) {
    if (pri > me->priority) {
      _PR_SET_RESCHED_FLAG();
    }
  }
#endif
}

Messung V0.5
C=94 H=91 G=92

¤ Dauer der Verarbeitung: 0.23 Sekunden  ¤

*© 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.