home *** CD-ROM | disk | FTP | other *** search
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /*
- * The contents of this file are subject to the Netscape Public License
- * Version 1.0 (the "NPL"); you may not use this file except in
- * compliance with the NPL. You may obtain a copy of the NPL at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the NPL is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
- * for the specific language governing rights and limitations under the
- * NPL.
- *
- * The Initial Developer of this code under the NPL is Netscape
- * Communications Corporation. Portions created by Netscape are
- * Copyright (C) 1998 Netscape Communications Corporation. All Rights
- * Reserved.
- */
-
- #include "primpl.h"
-
- _PRCPU *_pr_primordialCPU;
-
- 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;
- PRInt32 _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(PRThread *thread, PRBool needQueue);
-
- void _PR_InitCPUs()
- {
- PRThread *me = _PR_MD_CURRENT_THREAD();
-
- _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 HAVE_CUSTOM_USER_THREADS
- _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
- #endif
-
- /* Now start the first CPU. */
- _pr_primordialCPU = _PR_CreateCPU(me, PR_TRUE);
- _pr_numCPU = 1;
-
- _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);
-
- _PR_MD_INIT_CPUS();
- }
-
-
- 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_PRIORITY_LAST + 1; 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.
- */
- static _PRCPU *_PR_CreateCPU(PRThread *thread, PRBool needQueue)
- {
- _PRCPU *cpu;
-
- /*
- ** Create 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.
- */
- cpu = PR_NEWZAP(_PRCPU);
- if (cpu) {
-
- cpu->last_clock = PR_IntervalNow();
-
- if (needQueue == PR_TRUE)
- cpu->queue = _PR_CreateCPUQueue();
- else
- cpu->queue = _PR_MD_CURRENT_CPU()->queue;
-
- if (!cpu->queue) {
- PR_DELETE(cpu);
- return NULL;
- }
-
- /* 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 NULL;
- }
- cpu->idle_thread->no_sched = 0;
-
- cpu->thread = thread;
-
- if (_pr_cpu_affinity_mask)
- PR_SetThreadAffinityMask(thread, _pr_cpu_affinity_mask);
-
- /* Created a new CPU */
- _PR_CPU_LIST_LOCK();
- cpu->id = _pr_cpuID++;
- PR_APPEND_LINK(&cpu->links, &_PR_CPUQ());
- _PR_CPU_LIST_UNLOCK();
- }
- return cpu;
- }
-
- /*
- ** This code is used during a cpu's initial creation.
- */
- static void _PR_RunCPU(void *unused)
- {
- #if defined(XP_MAC)
- #pragma unused (unused)
- #endif
-
- _PRCPU *cpu;
- PRThread *me = _PR_MD_CURRENT_THREAD();
-
- PR_ASSERT(NULL != me);
-
- #ifdef HAVE_CUSTOM_USER_THREADS
- _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
- #endif
-
- me->no_sched = 1;
- cpu = _PR_CreateCPU(me, PR_TRUE);
-
- _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);
- }
- }
-
- 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_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);
-
- #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)
-
- PRUintn newCPU;
- PRThread *cpu;
-
- if (!_pr_initialized) _PR_ImplicitInitialization();
-
- _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_CreateThread(PR_SYSTEM_THREAD,
- _PR_RunCPU,
- NULL,
- 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();
- }
-