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


Quelle  prucpu.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"

_PRCPU* _pr_primordialCPU = NULL;

PRInt32 _pr_md_idle_cpus; /* number of idle cpus */
/*
 * The idle threads in MxN models increment/decrement _pr_md_idle_cpus.
 * If _PR_HAVE_ATOMIC_OPS is not defined, they can't use the atomic
 * increment/decrement routines (which are based on PR_Lock/PR_Unlock),
 * because PR_Lock asserts that the calling thread is not an idle thread.
 * So we use a _MDLock to protect _pr_md_idle_cpus.
 */

#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
#  ifndef _PR_HAVE_ATOMIC_OPS
static _MDLock _pr_md_idle_cpus_lock;
#  endif
#endif
PRUintn _pr_numCPU;
PRInt32 _pr_cpus_exit;
PRUint32 _pr_cpu_affinity_mask = 0;

#if !defined(_PR_GLOBAL_THREADS_ONLY)

static PRUintn _pr_cpuID;

static void PR_CALLBACK _PR_CPU_Idle(void*);

static _PRCPU* _PR_CreateCPU(void);
static PRStatus _PR_StartCPU(_PRCPU* cpu, PRThread* thread);

#  if !defined(_PR_LOCAL_THREADS_ONLY)
static void _PR_RunCPU(void* arg);
#  endif

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

  if (_native_threads_only) {
    return;
  }

  _pr_cpuID = 0;
  _MD_NEW_LOCK(&_pr_cpuLock);
#  if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
#    ifndef _PR_HAVE_ATOMIC_OPS
  _MD_NEW_LOCK(&_pr_md_idle_cpus_lock);
#    endif
#  endif

#  ifdef _PR_LOCAL_THREADS_ONLY

#    ifdef HAVE_CUSTOM_USER_THREADS
  _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
#    endif

  /* Now start the first CPU. */
  _pr_primordialCPU = _PR_CreateCPU();
  _pr_numCPU = 1;
  _PR_StartCPU(_pr_primordialCPU, me);

  _PR_MD_SET_CURRENT_CPU(_pr_primordialCPU);

  /* Initialize cpu for current thread (could be different from me) */
  _PR_MD_CURRENT_THREAD()->cpu = _pr_primordialCPU;

  _PR_MD_SET_LAST_THREAD(me);

#  else /* Combined MxN model */

  _pr_primordialCPU = _PR_CreateCPU();
  _pr_numCPU = 1;
  _PR_CreateThread(PR_SYSTEM_THREAD, _PR_RunCPU, _pr_primordialCPU,
                   PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD,
                   0, _PR_IDLE_THREAD);

#  endif /* _PR_LOCAL_THREADS_ONLY */

  _PR_MD_INIT_CPUS();
}

#  ifdef WINNT
/*
 * Right now this function merely stops the CPUs and does
 * not do any other cleanup.
 *
 * It is only implemented for WINNT because bug 161998 only
 * affects the WINNT version of NSPR, but it would be nice
 * to implement this function for other platforms too.
 */

void _PR_CleanupCPUs(void) {
  PRUintn i;
  PRCList* qp;
  _PRCPU* cpu;

  _pr_cpus_exit = 1;
  for (i = 0; i < _pr_numCPU; i++) {
    _PR_MD_WAKEUP_WAITER(NULL);
  }
  for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) {
    cpu = _PR_CPU_PTR(qp);
    _PR_MD_JOIN_THREAD(&cpu->thread->md);
  }
}
#  endif

static _PRCPUQueue* _PR_CreateCPUQueue(void) {
  PRInt32 index;
  _PRCPUQueue* cpuQueue;
  cpuQueue = PR_NEWZAP(_PRCPUQueue);

  _MD_NEW_LOCK(&cpuQueue->runQLock);
  _MD_NEW_LOCK(&cpuQueue->sleepQLock);
  _MD_NEW_LOCK(&cpuQueue->miscQLock);

  for (index = 0; index < PR_ARRAY_SIZE(cpuQueue->runQ); index++) {
    PR_INIT_CLIST(&(cpuQueue->runQ[index]));
  }
  PR_INIT_CLIST(&(cpuQueue->sleepQ));
  PR_INIT_CLIST(&(cpuQueue->pauseQ));
  PR_INIT_CLIST(&(cpuQueue->suspendQ));
  PR_INIT_CLIST(&(cpuQueue->waitingToJoinQ));

  cpuQueue->numCPUs = 1;

  return cpuQueue;
}

/*
 * Create a new CPU.
 *
 * This function initializes enough of the _PRCPU structure so
 * that it can be accessed safely by a global thread or another
 * CPU.  This function does not create the native thread that
 * will run the CPU nor does it initialize the parts of _PRCPU
 * that must be initialized by that native thread.
 *
 * The reason we cannot simply have the native thread create
 * and fully initialize a new CPU is that we need to be able to
 * create a usable _pr_primordialCPU in _PR_InitCPUs without
 * assuming that the primordial CPU thread we created can run
 * during NSPR initialization.  For example, on Windows while
 * new threads can be created by DllMain, they won't be able
 * to run during DLL initialization.  If NSPR is initialized
 * by DllMain, the primordial CPU thread won't run until DLL
 * initialization is finished.
 */

static _PRCPU* _PR_CreateCPU(void) {
  _PRCPU* cpu;

  cpu = PR_NEWZAP(_PRCPU);
  if (cpu) {
    cpu->queue = _PR_CreateCPUQueue();
    if (!cpu->queue) {
      PR_DELETE(cpu);
      return NULL;
    }
  }
  return cpu;
}

/*
 * Start a new CPU.
 *
 * 'cpu' is a _PRCPU structure created by _PR_CreateCPU().
 * 'thread' is the native thread that will run the CPU.
 *
 * If this function fails, 'cpu' is destroyed.
 */

static PRStatus _PR_StartCPU(_PRCPU* cpu, PRThread* thread) {
  /*
  ** Start a new cpu. The assumption this code makes is that the
  ** underlying operating system creates a stack to go with the new
  ** native thread. That stack will be used by the cpu when pausing.
  */


  PR_ASSERT(!_native_threads_only);

  cpu->last_clock = PR_IntervalNow();

  /* Before we create any threads on this CPU we have to
   * set the current CPU
   */

  _PR_MD_SET_CURRENT_CPU(cpu);
  _PR_MD_INIT_RUNNING_CPU(cpu);
  thread->cpu = cpu;

  cpu->idle_thread = _PR_CreateThread(
      PR_SYSTEM_THREAD, _PR_CPU_Idle, (void*)cpu, PR_PRIORITY_NORMAL,
      PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0, _PR_IDLE_THREAD);

  if (!cpu->idle_thread) {
    /* didn't clean up CPU queue XXXMB */
    PR_DELETE(cpu);
    return PR_FAILURE;
  }
  PR_ASSERT(cpu->idle_thread->cpu == cpu);

  cpu->idle_thread->no_sched = 0;

  cpu->thread = thread;

  if (_pr_cpu_affinity_mask) {
    PR_SetThreadAffinityMask(thread, _pr_cpu_affinity_mask);
  }

  /* Created and started a new CPU */
  _PR_CPU_LIST_LOCK();
  cpu->id = _pr_cpuID++;
  PR_APPEND_LINK(&cpu->links, &_PR_CPUQ());
  _PR_CPU_LIST_UNLOCK();

  return PR_SUCCESS;
}

#  if !defined(_PR_GLOBAL_THREADS_ONLY) && !defined(_PR_LOCAL_THREADS_ONLY)
/*
** This code is used during a cpu's initial creation.
*/

static void _PR_RunCPU(void* arg) {
  _PRCPU* cpu = (_PRCPU*)arg;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  PR_ASSERT(NULL != me);

  /*
   * _PR_StartCPU calls _PR_CreateThread to create the
   * idle thread.  Because _PR_CreateThread calls PR_Lock,
   * the current thread has to remain a global thread
   * during the _PR_StartCPU call so that it can wait for
   * the lock if the lock is held by another thread.  If
   * we clear the _PR_GLOBAL_SCOPE flag in
   * _PR_MD_CREATE_PRIMORDIAL_THREAD, the current thread
   * will be treated as a local thread and have trouble
   * waiting for the lock because the CPU is not fully
   * constructed yet.
   *
   * After the CPU is started, it is safe to mark the
   * current thread as a local thread.
   */


#    ifdef HAVE_CUSTOM_USER_THREADS
  _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
#    endif

  me->no_sched = 1;
  _PR_StartCPU(cpu, me);

#    ifdef HAVE_CUSTOM_USER_THREADS
  me->flags &= (~_PR_GLOBAL_SCOPE);
#    endif

  _PR_MD_SET_CURRENT_CPU(cpu);
  _PR_MD_SET_CURRENT_THREAD(cpu->thread);
  me->cpu = cpu;

  while (1) {
    PRInt32 is;
    if (!_PR_IS_NATIVE_THREAD(me)) {
      _PR_INTSOFF(is);
    }
    _PR_MD_START_INTERRUPTS();
    _PR_MD_SWITCH_CONTEXT(me);
  }
}
#  endif

static void PR_CALLBACK _PR_CPU_Idle(void* _cpu) {
  _PRCPU* cpu = (_PRCPU*)_cpu;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  PR_ASSERT(NULL != me);

  me->cpu = cpu;
  cpu->idle_thread = me;
  if (_MD_LAST_THREAD()) {
    _MD_LAST_THREAD()->no_sched = 0;
  }
  if (!_PR_IS_NATIVE_THREAD(me)) {
    _PR_MD_SET_INTSOFF(0);
  }
  while (1) {
    PRInt32 is;
    PRIntervalTime timeout;
    if (!_PR_IS_NATIVE_THREAD(me)) {
      _PR_INTSOFF(is);
    }

    _PR_RUNQ_LOCK(cpu);
#  if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
#    ifdef _PR_HAVE_ATOMIC_OPS
    _PR_MD_ATOMIC_INCREMENT(&_pr_md_idle_cpus);
#    else
    _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
    _pr_md_idle_cpus++;
    _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
#    endif /* _PR_HAVE_ATOMIC_OPS */
#  endif
    /* If someone on runq; do a nonblocking PAUSECPU */
    if (_PR_RUNQREADYMASK(me->cpu) != 0) {
      _PR_RUNQ_UNLOCK(cpu);
      timeout = PR_INTERVAL_NO_WAIT;
    } else {
      _PR_RUNQ_UNLOCK(cpu);

      _PR_SLEEPQ_LOCK(cpu);
      if (PR_CLIST_IS_EMPTY(&_PR_SLEEPQ(me->cpu))) {
        timeout = PR_INTERVAL_NO_TIMEOUT;
      } else {
        PRThread* wakeThread;
        wakeThread = _PR_THREAD_PTR(_PR_SLEEPQ(me->cpu).next);
        timeout = wakeThread->sleep;
      }
      _PR_SLEEPQ_UNLOCK(cpu);
    }

    /* Wait for an IO to complete */
    (void)_PR_MD_PAUSE_CPU(timeout);

#  ifdef WINNT
    if (_pr_cpus_exit) {
      /* _PR_CleanupCPUs tells us to exit */
      _PR_MD_END_THREAD();
    }
#  endif

#  if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
#    ifdef _PR_HAVE_ATOMIC_OPS
    _PR_MD_ATOMIC_DECREMENT(&_pr_md_idle_cpus);
#    else
    _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
    _pr_md_idle_cpus--;
    _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
#    endif /* _PR_HAVE_ATOMIC_OPS */
#  endif

    _PR_ClockInterrupt();

    /* Now schedule any thread that is on the runq
     * INTS must be OFF when calling PR_Schedule()
     */

    me->state = _PR_RUNNABLE;
    _PR_MD_SWITCH_CONTEXT(me);
    if (!_PR_IS_NATIVE_THREAD(me)) {
      _PR_FAST_INTSON(is);
    }
  }
}
#endif /* _PR_GLOBAL_THREADS_ONLY */

PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs) {
#if defined(_PR_GLOBAL_THREADS_ONLY) || defined(_PR_LOCAL_THREADS_ONLY)

  /* do nothing */

#else /* combined, MxN thread model */

  PRUintn newCPU;
  _PRCPU* cpu;
  PRThread* thr;

  if (!_pr_initialized) {
    _PR_ImplicitInitialization();
  }

  if (_native_threads_only) {
    return;
  }

  _PR_CPU_LIST_LOCK();
  if (_pr_numCPU < numCPUs) {
    newCPU = numCPUs - _pr_numCPU;
    _pr_numCPU = numCPUs;
  } else {
    newCPU = 0;
  }
  _PR_CPU_LIST_UNLOCK();

  for (; newCPU; newCPU--) {
    cpu = _PR_CreateCPU();
    thr = _PR_CreateThread(PR_SYSTEM_THREAD, _PR_RunCPU, cpu,
                           PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
                           PR_UNJOINABLE_THREAD, 0, _PR_IDLE_THREAD);
  }
#endif
}

PR_IMPLEMENT(_PRCPU*) _PR_GetPrimordialCPU(void) {
  if (_pr_primordialCPU) {
    return _pr_primordialCPU;
  } else {
    return _PR_MD_CURRENT_CPU();
  }
}

Messung V0.5
C=95 H=97 G=95

¤ Dauer der Verarbeitung: 0.12 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