nsprpub/pr/src/threads/combined/prucpu.c

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "primpl.h"
     8 _PRCPU *_pr_primordialCPU = NULL;
    10 PRInt32 _pr_md_idle_cpus;       /* number of idle cpus */
    11 /*
    12  * The idle threads in MxN models increment/decrement _pr_md_idle_cpus.
    13  * If _PR_HAVE_ATOMIC_OPS is not defined, they can't use the atomic
    14  * increment/decrement routines (which are based on PR_Lock/PR_Unlock),
    15  * because PR_Lock asserts that the calling thread is not an idle thread.
    16  * So we use a _MDLock to protect _pr_md_idle_cpus.
    17  */
    18 #if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
    19 #ifndef _PR_HAVE_ATOMIC_OPS
    20 static _MDLock _pr_md_idle_cpus_lock;
    21 #endif
    22 #endif
    23 PRUintn _pr_numCPU;
    24 PRInt32 _pr_cpus_exit;
    25 PRUint32 _pr_cpu_affinity_mask = 0;
    27 #if !defined (_PR_GLOBAL_THREADS_ONLY)
    29 static PRUintn _pr_cpuID;
    31 static void PR_CALLBACK _PR_CPU_Idle(void *);
    33 static _PRCPU *_PR_CreateCPU(void);
    34 static PRStatus _PR_StartCPU(_PRCPU *cpu, PRThread *thread);
    36 #if !defined(_PR_LOCAL_THREADS_ONLY)
    37 static void _PR_RunCPU(void *arg);
    38 #endif
    40 void  _PR_InitCPUs()
    41 {
    42     PRThread *me = _PR_MD_CURRENT_THREAD();
    44     if (_native_threads_only)
    45         return;
    47     _pr_cpuID = 0;
    48     _MD_NEW_LOCK( &_pr_cpuLock);
    49 #if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
    50 #ifndef _PR_HAVE_ATOMIC_OPS
    51     _MD_NEW_LOCK(&_pr_md_idle_cpus_lock);
    52 #endif
    53 #endif
    55 #ifdef _PR_LOCAL_THREADS_ONLY
    57 #ifdef HAVE_CUSTOM_USER_THREADS
    58     _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
    59 #endif
    61     /* Now start the first CPU. */
    62     _pr_primordialCPU = _PR_CreateCPU();
    63     _pr_numCPU = 1;
    64     _PR_StartCPU(_pr_primordialCPU, me);
    66     _PR_MD_SET_CURRENT_CPU(_pr_primordialCPU);
    68     /* Initialize cpu for current thread (could be different from me) */
    69     _PR_MD_CURRENT_THREAD()->cpu = _pr_primordialCPU;
    71     _PR_MD_SET_LAST_THREAD(me);
    73 #else /* Combined MxN model */
    75     _pr_primordialCPU = _PR_CreateCPU();
    76     _pr_numCPU = 1;
    77     _PR_CreateThread(PR_SYSTEM_THREAD,
    78                      _PR_RunCPU,
    79                      _pr_primordialCPU,
    80                      PR_PRIORITY_NORMAL,
    81                      PR_GLOBAL_THREAD,
    82                      PR_UNJOINABLE_THREAD,
    83                      0,
    84                      _PR_IDLE_THREAD);
    86 #endif /* _PR_LOCAL_THREADS_ONLY */
    88     _PR_MD_INIT_CPUS();
    89 }
    91 #ifdef WINNT
    92 /*
    93  * Right now this function merely stops the CPUs and does
    94  * not do any other cleanup.
    95  *
    96  * It is only implemented for WINNT because bug 161998 only
    97  * affects the WINNT version of NSPR, but it would be nice
    98  * to implement this function for other platforms too.
    99  */
   100 void _PR_CleanupCPUs(void)
   101 {
   102     PRUintn i;
   103     PRCList *qp;
   104     _PRCPU *cpu;
   106     _pr_cpus_exit = 1;
   107     for (i = 0; i < _pr_numCPU; i++) {
   108         _PR_MD_WAKEUP_WAITER(NULL);
   109     }
   110     for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) {
   111         cpu = _PR_CPU_PTR(qp);
   112         _PR_MD_JOIN_THREAD(&cpu->thread->md);
   113     }
   114 }
   115 #endif
   117 static _PRCPUQueue *_PR_CreateCPUQueue(void)
   118 {
   119     PRInt32 index;
   120     _PRCPUQueue *cpuQueue;
   121     cpuQueue = PR_NEWZAP(_PRCPUQueue);
   123     _MD_NEW_LOCK( &cpuQueue->runQLock );
   124     _MD_NEW_LOCK( &cpuQueue->sleepQLock );
   125     _MD_NEW_LOCK( &cpuQueue->miscQLock );
   127     for (index = 0; index < PR_PRIORITY_LAST + 1; index++)
   128         PR_INIT_CLIST( &(cpuQueue->runQ[index]) );
   129     PR_INIT_CLIST( &(cpuQueue->sleepQ) );
   130     PR_INIT_CLIST( &(cpuQueue->pauseQ) );
   131     PR_INIT_CLIST( &(cpuQueue->suspendQ) );
   132     PR_INIT_CLIST( &(cpuQueue->waitingToJoinQ) );
   134     cpuQueue->numCPUs = 1;
   136     return cpuQueue;
   137 }
   139 /*
   140  * Create a new CPU.
   141  *
   142  * This function initializes enough of the _PRCPU structure so
   143  * that it can be accessed safely by a global thread or another
   144  * CPU.  This function does not create the native thread that
   145  * will run the CPU nor does it initialize the parts of _PRCPU
   146  * that must be initialized by that native thread.
   147  *
   148  * The reason we cannot simply have the native thread create
   149  * and fully initialize a new CPU is that we need to be able to
   150  * create a usable _pr_primordialCPU in _PR_InitCPUs without
   151  * assuming that the primordial CPU thread we created can run
   152  * during NSPR initialization.  For example, on Windows while
   153  * new threads can be created by DllMain, they won't be able
   154  * to run during DLL initialization.  If NSPR is initialized
   155  * by DllMain, the primordial CPU thread won't run until DLL
   156  * initialization is finished.
   157  */
   158 static _PRCPU *_PR_CreateCPU(void)
   159 {
   160     _PRCPU *cpu;
   162     cpu = PR_NEWZAP(_PRCPU);
   163     if (cpu) {
   164         cpu->queue = _PR_CreateCPUQueue();
   165         if (!cpu->queue) {
   166             PR_DELETE(cpu);
   167             return NULL;
   168         }
   169     }
   170     return cpu;
   171 }
   173 /*
   174  * Start a new CPU.
   175  *
   176  * 'cpu' is a _PRCPU structure created by _PR_CreateCPU().
   177  * 'thread' is the native thread that will run the CPU.
   178  *
   179  * If this function fails, 'cpu' is destroyed.
   180  */
   181 static PRStatus _PR_StartCPU(_PRCPU *cpu, PRThread *thread)
   182 {
   183     /*
   184     ** Start a new cpu. The assumption this code makes is that the
   185     ** underlying operating system creates a stack to go with the new
   186     ** native thread. That stack will be used by the cpu when pausing.
   187     */
   189     PR_ASSERT(!_native_threads_only);
   191     cpu->last_clock = PR_IntervalNow();
   193     /* Before we create any threads on this CPU we have to
   194      * set the current CPU 
   195      */
   196     _PR_MD_SET_CURRENT_CPU(cpu);
   197     _PR_MD_INIT_RUNNING_CPU(cpu);
   198     thread->cpu = cpu;
   200     cpu->idle_thread = _PR_CreateThread(PR_SYSTEM_THREAD,
   201                                         _PR_CPU_Idle,
   202                                         (void *)cpu,
   203                                         PR_PRIORITY_NORMAL,
   204                                         PR_LOCAL_THREAD,
   205                                         PR_UNJOINABLE_THREAD,
   206                                         0,
   207                                         _PR_IDLE_THREAD);
   209     if (!cpu->idle_thread) {
   210         /* didn't clean up CPU queue XXXMB */
   211         PR_DELETE(cpu);
   212         return PR_FAILURE;
   213     } 
   214     PR_ASSERT(cpu->idle_thread->cpu == cpu);
   216     cpu->idle_thread->no_sched = 0;
   218     cpu->thread = thread;
   220     if (_pr_cpu_affinity_mask)
   221         PR_SetThreadAffinityMask(thread, _pr_cpu_affinity_mask);
   223     /* Created and started a new CPU */
   224     _PR_CPU_LIST_LOCK();
   225     cpu->id = _pr_cpuID++;
   226     PR_APPEND_LINK(&cpu->links, &_PR_CPUQ());
   227     _PR_CPU_LIST_UNLOCK();
   229     return PR_SUCCESS;
   230 }
   232 #if !defined(_PR_GLOBAL_THREADS_ONLY) && !defined(_PR_LOCAL_THREADS_ONLY)
   233 /*
   234 ** This code is used during a cpu's initial creation.
   235 */
   236 static void _PR_RunCPU(void *arg)
   237 {
   238     _PRCPU *cpu = (_PRCPU *)arg;
   239     PRThread *me = _PR_MD_CURRENT_THREAD();
   241     PR_ASSERT(NULL != me);
   243     /*
   244      * _PR_StartCPU calls _PR_CreateThread to create the
   245      * idle thread.  Because _PR_CreateThread calls PR_Lock,
   246      * the current thread has to remain a global thread
   247      * during the _PR_StartCPU call so that it can wait for
   248      * the lock if the lock is held by another thread.  If
   249      * we clear the _PR_GLOBAL_SCOPE flag in
   250      * _PR_MD_CREATE_PRIMORDIAL_THREAD, the current thread
   251      * will be treated as a local thread and have trouble
   252      * waiting for the lock because the CPU is not fully
   253      * constructed yet.
   254      *
   255      * After the CPU is started, it is safe to mark the
   256      * current thread as a local thread.
   257      */
   259 #ifdef HAVE_CUSTOM_USER_THREADS
   260     _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
   261 #endif
   263     me->no_sched = 1;
   264     _PR_StartCPU(cpu, me);
   266 #ifdef HAVE_CUSTOM_USER_THREADS
   267     me->flags &= (~_PR_GLOBAL_SCOPE);
   268 #endif
   270     _PR_MD_SET_CURRENT_CPU(cpu);
   271     _PR_MD_SET_CURRENT_THREAD(cpu->thread);
   272     me->cpu = cpu;
   274     while(1) {
   275         PRInt32 is;
   276         if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is);
   277 	    _PR_MD_START_INTERRUPTS();
   278         _PR_MD_SWITCH_CONTEXT(me);
   279     }
   280 }
   281 #endif
   283 static void PR_CALLBACK _PR_CPU_Idle(void *_cpu)
   284 {
   285     _PRCPU *cpu = (_PRCPU *)_cpu;
   286     PRThread *me = _PR_MD_CURRENT_THREAD();
   288     PR_ASSERT(NULL != me);
   290     me->cpu = cpu;
   291     cpu->idle_thread = me;
   292     if (_MD_LAST_THREAD())
   293         _MD_LAST_THREAD()->no_sched = 0;
   294     if (!_PR_IS_NATIVE_THREAD(me)) _PR_MD_SET_INTSOFF(0);
   295     while(1) {
   296         PRInt32 is;
   297         PRIntervalTime timeout;
   298         if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is);
   300         _PR_RUNQ_LOCK(cpu);
   301 #if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
   302 #ifdef _PR_HAVE_ATOMIC_OPS
   303         _PR_MD_ATOMIC_INCREMENT(&_pr_md_idle_cpus);
   304 #else
   305         _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
   306         _pr_md_idle_cpus++;
   307         _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
   308 #endif /* _PR_HAVE_ATOMIC_OPS */
   309 #endif
   310         /* If someone on runq; do a nonblocking PAUSECPU */
   311         if (_PR_RUNQREADYMASK(me->cpu) != 0) {
   312             _PR_RUNQ_UNLOCK(cpu);
   313             timeout = PR_INTERVAL_NO_WAIT;
   314         } else {
   315             _PR_RUNQ_UNLOCK(cpu);
   317             _PR_SLEEPQ_LOCK(cpu);
   318             if (PR_CLIST_IS_EMPTY(&_PR_SLEEPQ(me->cpu))) {
   319                 timeout = PR_INTERVAL_NO_TIMEOUT;
   320             } else {
   321                 PRThread *wakeThread;
   322                 wakeThread = _PR_THREAD_PTR(_PR_SLEEPQ(me->cpu).next);
   323                 timeout = wakeThread->sleep;
   324             }
   325             _PR_SLEEPQ_UNLOCK(cpu);
   326         }
   328         /* Wait for an IO to complete */
   329         (void)_PR_MD_PAUSE_CPU(timeout);
   331 #ifdef WINNT
   332         if (_pr_cpus_exit) {
   333             /* _PR_CleanupCPUs tells us to exit */
   334             _PR_MD_END_THREAD();
   335         }
   336 #endif
   338 #if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
   339 #ifdef _PR_HAVE_ATOMIC_OPS
   340         _PR_MD_ATOMIC_DECREMENT(&_pr_md_idle_cpus);
   341 #else
   342         _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
   343         _pr_md_idle_cpus--;
   344         _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
   345 #endif /* _PR_HAVE_ATOMIC_OPS */
   346 #endif
   348 		_PR_ClockInterrupt();
   350 		/* Now schedule any thread that is on the runq
   351 		 * INTS must be OFF when calling PR_Schedule()
   352 		 */
   353 		me->state = _PR_RUNNABLE;
   354 		_PR_MD_SWITCH_CONTEXT(me);
   355 		if (!_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is);
   356     }
   357 }
   358 #endif /* _PR_GLOBAL_THREADS_ONLY */
   360 PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs)
   361 {
   362 #if defined(_PR_GLOBAL_THREADS_ONLY) || defined(_PR_LOCAL_THREADS_ONLY)
   364     /* do nothing */
   366 #else /* combined, MxN thread model */
   368     PRUintn newCPU;
   369     _PRCPU *cpu;
   370     PRThread *thr;
   373     if (!_pr_initialized) _PR_ImplicitInitialization();
   375 	if (_native_threads_only)
   376 		return;
   378     _PR_CPU_LIST_LOCK();
   379     if (_pr_numCPU < numCPUs) {
   380         newCPU = numCPUs - _pr_numCPU;
   381         _pr_numCPU = numCPUs;
   382     } else newCPU = 0;
   383     _PR_CPU_LIST_UNLOCK();
   385     for (; newCPU; newCPU--) {
   386         cpu = _PR_CreateCPU();
   387         thr = _PR_CreateThread(PR_SYSTEM_THREAD,
   388                               _PR_RunCPU,
   389                               cpu,
   390                               PR_PRIORITY_NORMAL,
   391                               PR_GLOBAL_THREAD,
   392                               PR_UNJOINABLE_THREAD,
   393                               0,
   394                               _PR_IDLE_THREAD);
   395     }
   396 #endif
   397 }
   399 PR_IMPLEMENT(_PRCPU *) _PR_GetPrimordialCPU(void)
   400 {
   401     if (_pr_primordialCPU)
   402         return _pr_primordialCPU;
   403     else
   404         return _PR_MD_CURRENT_CPU();
   405 }

mercurial