Back to index

lightning-sunbird  0.9+nobinonly
prucpu.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "primpl.h"
00039 
00040 _PRCPU *_pr_primordialCPU = NULL;
00041 
00042 PRInt32 _pr_md_idle_cpus;       /* number of idle cpus */
00043 /*
00044  * The idle threads in MxN models increment/decrement _pr_md_idle_cpus.
00045  * If _PR_HAVE_ATOMIC_OPS is not defined, they can't use the atomic
00046  * increment/decrement routines (which are based on PR_Lock/PR_Unlock),
00047  * because PR_Lock asserts that the calling thread is not an idle thread.
00048  * So we use a _MDLock to protect _pr_md_idle_cpus.
00049  */
00050 #if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
00051 #ifndef _PR_HAVE_ATOMIC_OPS
00052 static _MDLock _pr_md_idle_cpus_lock;
00053 #endif
00054 #endif
00055 PRUintn _pr_numCPU;
00056 PRInt32 _pr_cpus_exit;
00057 PRInt32 _pr_cpu_affinity_mask = 0;
00058 
00059 #if !defined (_PR_GLOBAL_THREADS_ONLY)
00060 
00061 static PRUintn _pr_cpuID;
00062 
00063 static void PR_CALLBACK _PR_CPU_Idle(void *);
00064 
00065 static _PRCPU *_PR_CreateCPU(void);
00066 static PRStatus _PR_StartCPU(_PRCPU *cpu, PRThread *thread);
00067 
00068 #if !defined(_PR_LOCAL_THREADS_ONLY)
00069 static void _PR_RunCPU(void *arg);
00070 #endif
00071 
00072 void  _PR_InitCPUs()
00073 {
00074     PRThread *me = _PR_MD_CURRENT_THREAD();
00075 
00076     if (_native_threads_only)
00077         return;
00078 
00079     _pr_cpuID = 0;
00080     _MD_NEW_LOCK( &_pr_cpuLock);
00081 #if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
00082 #ifndef _PR_HAVE_ATOMIC_OPS
00083     _MD_NEW_LOCK(&_pr_md_idle_cpus_lock);
00084 #endif
00085 #endif
00086 
00087 #ifdef _PR_LOCAL_THREADS_ONLY
00088 
00089 #ifdef HAVE_CUSTOM_USER_THREADS
00090     _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
00091 #endif
00092 
00093     /* Now start the first CPU. */
00094     _pr_primordialCPU = _PR_CreateCPU();
00095     _pr_numCPU = 1;
00096     _PR_StartCPU(_pr_primordialCPU, me);
00097 
00098     _PR_MD_SET_CURRENT_CPU(_pr_primordialCPU);
00099 
00100     /* Initialize cpu for current thread (could be different from me) */
00101     _PR_MD_CURRENT_THREAD()->cpu = _pr_primordialCPU;
00102 
00103     _PR_MD_SET_LAST_THREAD(me);
00104 
00105 #else /* Combined MxN model */
00106 
00107     _pr_primordialCPU = _PR_CreateCPU();
00108     _pr_numCPU = 1;
00109     _PR_CreateThread(PR_SYSTEM_THREAD,
00110                      _PR_RunCPU,
00111                      _pr_primordialCPU,
00112                      PR_PRIORITY_NORMAL,
00113                      PR_GLOBAL_THREAD,
00114                      PR_UNJOINABLE_THREAD,
00115                      0,
00116                      _PR_IDLE_THREAD);
00117 
00118 #endif /* _PR_LOCAL_THREADS_ONLY */
00119 
00120     _PR_MD_INIT_CPUS();
00121 }
00122 
00123 #ifdef WINNT
00124 /*
00125  * Right now this function merely stops the CPUs and does
00126  * not do any other cleanup.
00127  *
00128  * It is only implemented for WINNT because bug 161998 only
00129  * affects the WINNT version of NSPR, but it would be nice
00130  * to implement this function for other platforms too.
00131  */
00132 void _PR_CleanupCPUs(void)
00133 {
00134     PRUintn i;
00135     PRCList *qp;
00136     _PRCPU *cpu;
00137 
00138     _pr_cpus_exit = 1;
00139     for (i = 0; i < _pr_numCPU; i++) {
00140         _PR_MD_WAKEUP_WAITER(NULL);
00141     }
00142     for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) {
00143         cpu = _PR_CPU_PTR(qp);
00144         _PR_MD_JOIN_THREAD(&cpu->thread->md);
00145     }
00146 }
00147 #endif
00148 
00149 static _PRCPUQueue *_PR_CreateCPUQueue(void)
00150 {
00151     PRInt32 index;
00152     _PRCPUQueue *cpuQueue;
00153     cpuQueue = PR_NEWZAP(_PRCPUQueue);
00154  
00155     _MD_NEW_LOCK( &cpuQueue->runQLock );
00156     _MD_NEW_LOCK( &cpuQueue->sleepQLock );
00157     _MD_NEW_LOCK( &cpuQueue->miscQLock );
00158 
00159     for (index = 0; index < PR_PRIORITY_LAST + 1; index++)
00160         PR_INIT_CLIST( &(cpuQueue->runQ[index]) );
00161     PR_INIT_CLIST( &(cpuQueue->sleepQ) );
00162     PR_INIT_CLIST( &(cpuQueue->pauseQ) );
00163     PR_INIT_CLIST( &(cpuQueue->suspendQ) );
00164     PR_INIT_CLIST( &(cpuQueue->waitingToJoinQ) );
00165 
00166     cpuQueue->numCPUs = 1;
00167 
00168     return cpuQueue;
00169 }
00170 
00171 /*
00172  * Create a new CPU.
00173  *
00174  * This function initializes enough of the _PRCPU structure so
00175  * that it can be accessed safely by a global thread or another
00176  * CPU.  This function does not create the native thread that
00177  * will run the CPU nor does it initialize the parts of _PRCPU
00178  * that must be initialized by that native thread.
00179  *
00180  * The reason we cannot simply have the native thread create
00181  * and fully initialize a new CPU is that we need to be able to
00182  * create a usable _pr_primordialCPU in _PR_InitCPUs without
00183  * assuming that the primordial CPU thread we created can run
00184  * during NSPR initialization.  For example, on Windows while
00185  * new threads can be created by DllMain, they won't be able
00186  * to run during DLL initialization.  If NSPR is initialized
00187  * by DllMain, the primordial CPU thread won't run until DLL
00188  * initialization is finished.
00189  */
00190 static _PRCPU *_PR_CreateCPU(void)
00191 {
00192     _PRCPU *cpu;
00193 
00194     cpu = PR_NEWZAP(_PRCPU);
00195     if (cpu) {
00196         cpu->queue = _PR_CreateCPUQueue();
00197         if (!cpu->queue) {
00198             PR_DELETE(cpu);
00199             return NULL;
00200         }
00201     }
00202     return cpu;
00203 }
00204 
00205 /*
00206  * Start a new CPU.
00207  *
00208  * 'cpu' is a _PRCPU structure created by _PR_CreateCPU().
00209  * 'thread' is the native thread that will run the CPU.
00210  *
00211  * If this function fails, 'cpu' is destroyed.
00212  */
00213 static PRStatus _PR_StartCPU(_PRCPU *cpu, PRThread *thread)
00214 {
00215     /*
00216     ** Start a new cpu. The assumption this code makes is that the
00217     ** underlying operating system creates a stack to go with the new
00218     ** native thread. That stack will be used by the cpu when pausing.
00219     */
00220 
00221     PR_ASSERT(!_native_threads_only);
00222 
00223     cpu->last_clock = PR_IntervalNow();
00224 
00225     /* Before we create any threads on this CPU we have to
00226      * set the current CPU 
00227      */
00228     _PR_MD_SET_CURRENT_CPU(cpu);
00229     _PR_MD_INIT_RUNNING_CPU(cpu);
00230     thread->cpu = cpu;
00231 
00232     cpu->idle_thread = _PR_CreateThread(PR_SYSTEM_THREAD,
00233                                         _PR_CPU_Idle,
00234                                         (void *)cpu,
00235                                         PR_PRIORITY_NORMAL,
00236                                         PR_LOCAL_THREAD,
00237                                         PR_UNJOINABLE_THREAD,
00238                                         0,
00239                                         _PR_IDLE_THREAD);
00240 
00241     if (!cpu->idle_thread) {
00242         /* didn't clean up CPU queue XXXMB */
00243         PR_DELETE(cpu);
00244         return PR_FAILURE;
00245     } 
00246     PR_ASSERT(cpu->idle_thread->cpu == cpu);
00247 
00248     cpu->idle_thread->no_sched = 0;
00249 
00250     cpu->thread = thread;
00251 
00252     if (_pr_cpu_affinity_mask)
00253         PR_SetThreadAffinityMask(thread, _pr_cpu_affinity_mask);
00254 
00255     /* Created and started a new CPU */
00256     _PR_CPU_LIST_LOCK();
00257     cpu->id = _pr_cpuID++;
00258     PR_APPEND_LINK(&cpu->links, &_PR_CPUQ());
00259     _PR_CPU_LIST_UNLOCK();
00260 
00261     return PR_SUCCESS;
00262 }
00263 
00264 #if !defined(_PR_GLOBAL_THREADS_ONLY) && !defined(_PR_LOCAL_THREADS_ONLY)
00265 /*
00266 ** This code is used during a cpu's initial creation.
00267 */
00268 static void _PR_RunCPU(void *arg)
00269 {
00270     _PRCPU *cpu = (_PRCPU *)arg;
00271     PRThread *me = _PR_MD_CURRENT_THREAD();
00272 
00273     PR_ASSERT(NULL != me);
00274 
00275     /*
00276      * _PR_StartCPU calls _PR_CreateThread to create the
00277      * idle thread.  Because _PR_CreateThread calls PR_Lock,
00278      * the current thread has to remain a global thread
00279      * during the _PR_StartCPU call so that it can wait for
00280      * the lock if the lock is held by another thread.  If
00281      * we clear the _PR_GLOBAL_SCOPE flag in
00282      * _PR_MD_CREATE_PRIMORDIAL_THREAD, the current thread
00283      * will be treated as a local thread and have trouble
00284      * waiting for the lock because the CPU is not fully
00285      * constructed yet.
00286      *
00287      * After the CPU is started, it is safe to mark the
00288      * current thread as a local thread.
00289      */
00290 
00291 #ifdef HAVE_CUSTOM_USER_THREADS
00292     _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
00293 #endif
00294 
00295     me->no_sched = 1;
00296     _PR_StartCPU(cpu, me);
00297 
00298 #ifdef HAVE_CUSTOM_USER_THREADS
00299     me->flags &= (~_PR_GLOBAL_SCOPE);
00300 #endif
00301 
00302     _PR_MD_SET_CURRENT_CPU(cpu);
00303     _PR_MD_SET_CURRENT_THREAD(cpu->thread);
00304     me->cpu = cpu;
00305 
00306     while(1) {
00307         PRInt32 is;
00308         if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is);
00309            _PR_MD_START_INTERRUPTS();
00310         _PR_MD_SWITCH_CONTEXT(me);
00311     }
00312 }
00313 #endif
00314 
00315 static void PR_CALLBACK _PR_CPU_Idle(void *_cpu)
00316 {
00317     _PRCPU *cpu = (_PRCPU *)_cpu;
00318     PRThread *me = _PR_MD_CURRENT_THREAD();
00319 
00320     PR_ASSERT(NULL != me);
00321 
00322     me->cpu = cpu;
00323     cpu->idle_thread = me;
00324     if (_MD_LAST_THREAD())
00325         _MD_LAST_THREAD()->no_sched = 0;
00326     if (!_PR_IS_NATIVE_THREAD(me)) _PR_MD_SET_INTSOFF(0);
00327     while(1) {
00328         PRInt32 is;
00329         PRIntervalTime timeout;
00330         if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is);
00331 
00332         _PR_RUNQ_LOCK(cpu);
00333 #if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
00334 #ifdef _PR_HAVE_ATOMIC_OPS
00335         _PR_MD_ATOMIC_INCREMENT(&_pr_md_idle_cpus);
00336 #else
00337         _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
00338         _pr_md_idle_cpus++;
00339         _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
00340 #endif /* _PR_HAVE_ATOMIC_OPS */
00341 #endif
00342         /* If someone on runq; do a nonblocking PAUSECPU */
00343         if (_PR_RUNQREADYMASK(me->cpu) != 0) {
00344             _PR_RUNQ_UNLOCK(cpu);
00345             timeout = PR_INTERVAL_NO_WAIT;
00346         } else {
00347             _PR_RUNQ_UNLOCK(cpu);
00348 
00349             _PR_SLEEPQ_LOCK(cpu);
00350             if (PR_CLIST_IS_EMPTY(&_PR_SLEEPQ(me->cpu))) {
00351                 timeout = PR_INTERVAL_NO_TIMEOUT;
00352             } else {
00353                 PRThread *wakeThread;
00354                 wakeThread = _PR_THREAD_PTR(_PR_SLEEPQ(me->cpu).next);
00355                 timeout = wakeThread->sleep;
00356             }
00357             _PR_SLEEPQ_UNLOCK(cpu);
00358         }
00359 
00360         /* Wait for an IO to complete */
00361         (void)_PR_MD_PAUSE_CPU(timeout);
00362 
00363 #ifdef WINNT
00364         if (_pr_cpus_exit) {
00365             /* _PR_CleanupCPUs tells us to exit */
00366             _PR_MD_END_THREAD();
00367         }
00368 #endif
00369 
00370 #if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
00371 #ifdef _PR_HAVE_ATOMIC_OPS
00372         _PR_MD_ATOMIC_DECREMENT(&_pr_md_idle_cpus);
00373 #else
00374         _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
00375         _pr_md_idle_cpus--;
00376         _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
00377 #endif /* _PR_HAVE_ATOMIC_OPS */
00378 #endif
00379 
00380               _PR_ClockInterrupt();
00381 
00382               /* Now schedule any thread that is on the runq
00383                * INTS must be OFF when calling PR_Schedule()
00384                */
00385               me->state = _PR_RUNNABLE;
00386               _PR_MD_SWITCH_CONTEXT(me);
00387               if (!_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is);
00388     }
00389 }
00390 #endif /* _PR_GLOBAL_THREADS_ONLY */
00391 
00392 PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs)
00393 {
00394 #if defined(_PR_GLOBAL_THREADS_ONLY) || defined(_PR_LOCAL_THREADS_ONLY)
00395 #ifdef XP_MAC 
00396 #pragma unused(numCPUs) 
00397 #endif
00398 
00399     /* do nothing */
00400 
00401 #else /* combined, MxN thread model */
00402 
00403     PRUintn newCPU;
00404     _PRCPU *cpu;
00405     PRThread *thr;
00406 
00407 
00408     if (!_pr_initialized) _PR_ImplicitInitialization();
00409 
00410        if (_native_threads_only)
00411               return;
00412     
00413     _PR_CPU_LIST_LOCK();
00414     if (_pr_numCPU < numCPUs) {
00415         newCPU = numCPUs - _pr_numCPU;
00416         _pr_numCPU = numCPUs;
00417     } else newCPU = 0;
00418     _PR_CPU_LIST_UNLOCK();
00419 
00420     for (; newCPU; newCPU--) {
00421         cpu = _PR_CreateCPU();
00422         thr = _PR_CreateThread(PR_SYSTEM_THREAD,
00423                               _PR_RunCPU,
00424                               cpu,
00425                               PR_PRIORITY_NORMAL,
00426                               PR_GLOBAL_THREAD,
00427                               PR_UNJOINABLE_THREAD,
00428                               0,
00429                               _PR_IDLE_THREAD);
00430     }
00431 #endif
00432 }
00433 
00434 PR_IMPLEMENT(_PRCPU *) _PR_GetPrimordialCPU(void)
00435 {
00436     if (_pr_primordialCPU)
00437         return _pr_primordialCPU;
00438     else
00439         return _PR_MD_CURRENT_CPU();
00440 }