diff options
Diffstat (limited to 'pr/src/threads/combined/prucpu.c')
-rw-r--r-- | pr/src/threads/combined/prucpu.c | 204 |
1 files changed, 130 insertions, 74 deletions
diff --git a/pr/src/threads/combined/prucpu.c b/pr/src/threads/combined/prucpu.c index d9159b52..3d575d6c 100644 --- a/pr/src/threads/combined/prucpu.c +++ b/pr/src/threads/combined/prucpu.c @@ -59,7 +59,8 @@ static PRUintn _pr_cpuID; static void PR_CALLBACK _PR_CPU_Idle(void *); -static _PRCPU *_PR_CreateCPU(PRThread *thread, PRBool needQueue); +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); @@ -69,6 +70,9 @@ 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) @@ -80,13 +84,13 @@ void _PR_InitCPUs() #ifdef _PR_LOCAL_THREADS_ONLY #ifdef HAVE_CUSTOM_USER_THREADS - if (!_native_threads_only) - _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me); + _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me); #endif /* Now start the first CPU. */ - _pr_primordialCPU = _PR_CreateCPU(me, PR_TRUE); + _pr_primordialCPU = _PR_CreateCPU(); _pr_numCPU = 1; + _PR_StartCPU(_pr_primordialCPU, me); _PR_MD_SET_CURRENT_CPU(_pr_primordialCPU); @@ -97,21 +101,47 @@ void _PR_InitCPUs() #else /* Combined MxN model */ + _pr_primordialCPU = _PR_CreateCPU(); + _pr_numCPU = 1; _PR_CreateThread(PR_SYSTEM_THREAD, _PR_RunCPU, - me, + _pr_primordialCPU, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0, _PR_IDLE_THREAD); - _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT); #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) { @@ -137,89 +167,113 @@ static _PRCPUQueue *_PR_CreateCPUQueue(void) /* * 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(PRThread *thread, PRBool needQueue) +static _PRCPU *_PR_CreateCPU(void) { _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; - + cpu->queue = _PR_CreateCPUQueue(); 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; - - if (!_native_threads_only) { - - 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->cpu = cpu; - - 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; } +/* + * 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 *cpu = (_PRCPU *)arg; PRThread *me = _PR_MD_CURRENT_THREAD(); - PRThread *waiter = (PRThread *) arg; PR_ASSERT(NULL != me); /* - * _PR_CreateCPU calls _PR_CreateThread to create the + * _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_CreateCPU call so that it can wait for + * 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 @@ -227,7 +281,7 @@ static void _PR_RunCPU(void *arg) * waiting for the lock because the CPU is not fully * constructed yet. * - * After the CPU is created, it is safe to mark the + * After the CPU is started, it is safe to mark the * current thread as a local thread. */ @@ -236,7 +290,7 @@ static void _PR_RunCPU(void *arg) #endif me->no_sched = 1; - cpu = _PR_CreateCPU(me, PR_TRUE); + _PR_StartCPU(cpu, me); #ifdef HAVE_CUSTOM_USER_THREADS me->flags &= (~_PR_GLOBAL_SCOPE); @@ -246,12 +300,6 @@ static void _PR_RunCPU(void *arg) _PR_MD_SET_CURRENT_THREAD(cpu->thread); me->cpu = cpu; - if (waiter) { - _pr_primordialCPU = cpu; - _pr_numCPU = 1; - _PR_MD_WAKEUP_WAITER(waiter); - } - while(1) { PRInt32 is; if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); @@ -309,6 +357,12 @@ static void PR_CALLBACK _PR_CPU_Idle(void *_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 @@ -344,7 +398,8 @@ PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs) #else /* combined, MxN thread model */ PRUintn newCPU; - PRThread *cpu; + _PRCPU *cpu; + PRThread *thr; if (!_pr_initialized) _PR_ImplicitInitialization(); @@ -360,9 +415,10 @@ PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs) _PR_CPU_LIST_UNLOCK(); for (; newCPU; newCPU--) { - cpu = _PR_CreateThread(PR_SYSTEM_THREAD, + cpu = _PR_CreateCPU(); + thr = _PR_CreateThread(PR_SYSTEM_THREAD, _PR_RunCPU, - NULL, + cpu, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, |