📄 pruthr.c
字号:
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape Portable Runtime (NSPR). * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */#include "primpl.h"#include <signal.h>#include <string.h>#if defined(WIN95) /*** Some local variables report warnings on Win95 because the code paths** using them are conditioned on HAVE_CUSTOME_USER_THREADS.** The pragma suppresses the warning.***/#pragma warning(disable : 4101)#endif #if defined(XP_MAC)#include <LowMem.h>#endif/* _pr_activeLock protects the following global variables */PRLock *_pr_activeLock;PRInt32 _pr_primordialExitCount; /* In PR_Cleanup(), the primordial thread * waits until all other user (non-system) * threads have terminated before it exits. * So whenever we decrement _pr_userActive, * it is compared with * _pr_primordialExitCount. * If the primordial thread is a system * thread, then _pr_primordialExitCount * is 0. If the primordial thread is * itself a user thread, then * _pr_primordialThread is 1. */PRCondVar *_pr_primordialExitCVar; /* When _pr_userActive is decremented to * _pr_primordialExitCount, this condition * variable is notified. */PRLock *_pr_deadQLock;PRUint32 _pr_numNativeDead;PRUint32 _pr_numUserDead;PRCList _pr_deadNativeQ;PRCList _pr_deadUserQ;PRUint32 _pr_join_counter;PRUint32 _pr_local_threads;PRUint32 _pr_global_threads;PRBool suspendAllOn = PR_FALSE;PRThread *suspendAllThread = NULL;extern PRCList _pr_active_global_threadQ;extern PRCList _pr_active_local_threadQ;static void _PR_DecrActiveThreadCount(PRThread *thread);static PRThread *_PR_AttachThread(PRThreadType, PRThreadPriority, PRThreadStack *);static void _PR_InitializeNativeStack(PRThreadStack *ts);static void _PR_InitializeRecycledThread(PRThread *thread);static void _PR_UserRunThread(void);void _PR_InitThreads(PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs){#if defined(XP_MAC)#pragma unused (maxPTDs)#endif PRThread *thread; PRThreadStack *stack; _pr_terminationCVLock = PR_NewLock(); _pr_activeLock = PR_NewLock();#ifndef HAVE_CUSTOM_USER_THREADS stack = PR_NEWZAP(PRThreadStack);#ifdef HAVE_STACK_GROWING_UP stack->stackTop = (char*) ((((long)&type) >> _pr_pageShift) << _pr_pageShift);#else#if defined(SOLARIS) || defined (UNIXWARE) && defined (USR_SVR4_THREADS) stack->stackTop = (char*) &thread;#elif defined(XP_MAC) stack->stackTop = (char*) LMGetCurStackBase();#else stack->stackTop = (char*) ((((long)&type + _pr_pageSize - 1) >> _pr_pageShift) << _pr_pageShift);#endif#endif#else /* If stack is NULL, we're using custom user threads like NT fibers. */ stack = PR_NEWZAP(PRThreadStack); if (stack) { stack->stackSize = 0; _PR_InitializeNativeStack(stack); }#endif /* HAVE_CUSTOM_USER_THREADS */ thread = _PR_AttachThread(type, priority, stack); if (thread) { _PR_MD_SET_CURRENT_THREAD(thread); if (type == PR_SYSTEM_THREAD) { thread->flags = _PR_SYSTEM; _pr_systemActive++; _pr_primordialExitCount = 0; } else { _pr_userActive++; _pr_primordialExitCount = 1; } thread->no_sched = 1; _pr_primordialExitCVar = PR_NewCondVar(_pr_activeLock); } if (!thread) PR_Abort();#ifdef _PR_LOCAL_THREADS_ONLY thread->flags |= _PR_PRIMORDIAL;#else thread->flags |= _PR_PRIMORDIAL | _PR_GLOBAL_SCOPE;#endif /* * Needs _PR_PRIMORDIAL flag set before calling * _PR_MD_INIT_THREAD() */ if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { /* * XXX do what? */ } if (_PR_IS_NATIVE_THREAD(thread)) { PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ()); _pr_global_threads++; } else { PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ()); _pr_local_threads++; } _pr_recycleThreads = 0; _pr_deadQLock = PR_NewLock(); _pr_numNativeDead = 0; _pr_numUserDead = 0; PR_INIT_CLIST(&_pr_deadNativeQ); PR_INIT_CLIST(&_pr_deadUserQ);}void _PR_CleanupThreads(void){ if (_pr_terminationCVLock) { PR_DestroyLock(_pr_terminationCVLock); _pr_terminationCVLock = NULL; } if (_pr_activeLock) { PR_DestroyLock(_pr_activeLock); _pr_activeLock = NULL; } if (_pr_primordialExitCVar) { PR_DestroyCondVar(_pr_primordialExitCVar); _pr_primordialExitCVar = NULL; } /* TODO _pr_dead{Native,User}Q need to be deleted */ if (_pr_deadQLock) { PR_DestroyLock(_pr_deadQLock); _pr_deadQLock = NULL; }}/*** Initialize a stack for a native thread*/static void _PR_InitializeNativeStack(PRThreadStack *ts){ if( ts && (ts->stackTop == 0) ) { ts->allocSize = ts->stackSize; /* ** Setup stackTop and stackBottom values. */#ifdef HAVE_STACK_GROWING_UP ts->allocBase = (char*) ((((long)&ts) >> _pr_pageShift) << _pr_pageShift); ts->stackBottom = ts->allocBase + ts->stackSize; ts->stackTop = ts->allocBase;#else ts->allocBase = (char*) ((((long)&ts + _pr_pageSize - 1) >> _pr_pageShift) << _pr_pageShift); ts->stackTop = ts->allocBase; ts->stackBottom = ts->allocBase - ts->stackSize;#endif }}void _PR_NotifyJoinWaiters(PRThread *thread){ /* ** Handle joinable threads. Change the state to waiting for join. ** Remove from our run Q and put it on global waiting to join Q. ** Notify on our "termination" condition variable so that joining ** thread will know about our termination. Switch our context and ** come back later on to continue the cleanup. */ PR_ASSERT(thread == _PR_MD_CURRENT_THREAD()); if (thread->term != NULL) { PR_Lock(_pr_terminationCVLock); _PR_THREAD_LOCK(thread); thread->state = _PR_JOIN_WAIT; if ( !_PR_IS_NATIVE_THREAD(thread) ) { _PR_MISCQ_LOCK(thread->cpu); _PR_ADD_JOINQ(thread, thread->cpu); _PR_MISCQ_UNLOCK(thread->cpu); } _PR_THREAD_UNLOCK(thread); PR_NotifyCondVar(thread->term); PR_Unlock(_pr_terminationCVLock); _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT); PR_ASSERT(thread->state != _PR_JOIN_WAIT); }}/* * Zero some of the data members of a recycled thread. * * Note that we can do this either when a dead thread is added to * the dead thread queue or when it is reused. Here, we are doing * this lazily, when the thread is reused in _PR_CreateThread(). */static void _PR_InitializeRecycledThread(PRThread *thread){ /* * Assert that the following data members are already zeroed * by _PR_CleanupThread(). */#ifdef DEBUG if (thread->privateData) { unsigned int i; for (i = 0; i < thread->tpdLength; i++) { PR_ASSERT(thread->privateData[i] == NULL); } }#endif PR_ASSERT(thread->dumpArg == 0 && thread->dump == 0); PR_ASSERT(thread->errorString == 0 && thread->errorStringSize == 0); PR_ASSERT(thread->errorStringLength == 0); /* Reset data members in thread structure */ thread->errorCode = thread->osErrorCode = 0; thread->io_pending = thread->io_suspended = PR_FALSE; thread->environment = 0; PR_INIT_CLIST(&thread->lockList);}PRStatus _PR_RecycleThread(PRThread *thread){ if ( _PR_IS_NATIVE_THREAD(thread) && _PR_NUM_DEADNATIVE < _pr_recycleThreads) { _PR_DEADQ_LOCK; PR_APPEND_LINK(&thread->links, &_PR_DEADNATIVEQ); _PR_INC_DEADNATIVE; _PR_DEADQ_UNLOCK; return (PR_SUCCESS); } else if ( !_PR_IS_NATIVE_THREAD(thread) && _PR_NUM_DEADUSER < _pr_recycleThreads) { _PR_DEADQ_LOCK; PR_APPEND_LINK(&thread->links, &_PR_DEADUSERQ); _PR_INC_DEADUSER; _PR_DEADQ_UNLOCK; return (PR_SUCCESS); } return (PR_FAILURE);}/* * Decrement the active thread count, either _pr_systemActive or * _pr_userActive, depending on whether the thread is a system thread * or a user thread. If all the user threads, except possibly * the primordial thread, have terminated, we notify the primordial * thread of this condition. * * Since this function will lock _pr_activeLock, do not call this * function while holding the _pr_activeLock lock, as this will result * in a deadlock. */static void_PR_DecrActiveThreadCount(PRThread *thread){ PR_Lock(_pr_activeLock); if (thread->flags & _PR_SYSTEM) { _pr_systemActive--; } else { _pr_userActive--; if (_pr_userActive == _pr_primordialExitCount) { PR_NotifyCondVar(_pr_primordialExitCVar); } } PR_Unlock(_pr_activeLock);}/*** Detach thread structure*/static void_PR_DestroyThread(PRThread *thread){ _PR_MD_FREE_LOCK(&thread->threadLock); PR_DELETE(thread);}void_PR_NativeDestroyThread(PRThread *thread){ if(thread->term) { PR_DestroyCondVar(thread->term); thread->term = 0; } if (NULL != thread->privateData) { PR_ASSERT(0 != thread->tpdLength); PR_DELETE(thread->privateData); thread->tpdLength = 0; } PR_DELETE(thread->stack); _PR_DestroyThread(thread);}void_PR_UserDestroyThread(PRThread *thread){ if(thread->term) { PR_DestroyCondVar(thread->term); thread->term = 0; } if (NULL != thread->privateData) { PR_ASSERT(0 != thread->tpdLength); PR_DELETE(thread->privateData); thread->tpdLength = 0; } _PR_MD_FREE_LOCK(&thread->threadLock); if (thread->threadAllocatedOnStack == 1) { _PR_MD_CLEAN_THREAD(thread); /* * Because the no_sched field is set, this thread/stack will * will not be re-used until the flag is cleared by the thread * we will context switch to. */ _PR_FreeStack(thread->stack); } else {#ifdef WINNT _PR_MD_CLEAN_THREAD(thread);#else /* * This assertion does not apply to NT. On NT, every fiber * has its threadAllocatedOnStack equal to 0. Elsewhere, * only the primordial thread has its threadAllocatedOnStack * equal to 0. */ PR_ASSERT(thread->flags & _PR_PRIMORDIAL);#endif }}/*** Run a thread's start function. When the start function returns the** thread is done executing and no longer needs the CPU. If there are no** more user threads running then we can exit the program.*/void _PR_NativeRunThread(void *arg){ PRThread *thread = (PRThread *)arg; _PR_MD_SET_CURRENT_THREAD(thread); _PR_MD_SET_CURRENT_CPU(NULL); /* Set up the thread stack information */ _PR_InitializeNativeStack(thread->stack); /* Set up the thread md information */ if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { /* * thread failed to initialize itself, possibly due to * failure to allocate per-thread resources */ return; } while(1) { thread->state = _PR_RUNNING; /* * Add to list of active threads */ PR_Lock(_pr_activeLock); PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ()); _pr_global_threads++; PR_Unlock(_pr_activeLock); (*thread->startFunc)(thread->arg); /* * The following two assertions are meant for NT asynch io. * * The thread should have no asynch io in progress when it * exits, otherwise the overlapped buffer, which is part of * the thread structure, would become invalid. */ PR_ASSERT(thread->io_pending == PR_FALSE); /* * This assertion enforces the programming guideline that * if an io function times out or is interrupted, the thread * should close the fd to force the asynch io to abort * before it exits. Right now, closing the fd is the only * way to clear the io_suspended flag. */ PR_ASSERT(thread->io_suspended == PR_FALSE); /* * remove thread from list of active threads */ PR_Lock(_pr_activeLock); PR_REMOVE_LINK(&thread->active); _pr_global_threads--; PR_Unlock(_pr_activeLock); PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting")); /* All done, time to go away */ _PR_CleanupThread(thread); _PR_NotifyJoinWaiters(thread); _PR_DecrActiveThreadCount(thread); thread->state = _PR_DEAD_STATE; if (!_pr_recycleThreads || (_PR_RecycleThread(thread) == PR_FAILURE)) { /* * thread not recycled * platform-specific thread exit processing * - for stuff like releasing native-thread resources, etc.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -