📄 ptthread.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. *//*** File: ptthread.c** Descritpion: Implemenation for threds using pthreds** Exports: ptthread.h*/#if defined(_PR_PTHREADS) || defined(_PR_DCETHREADS)#include "prlog.h"#include "primpl.h"#include "prpdce.h"#include <pthread.h>#include <unistd.h>#include <string.h>#include <signal.h>/* * Record whether or not we have the privilege to set the scheduling * policy and priority of threads. 0 means that privilege is available. * EPERM means that privilege is not available. */static PRIntn pt_schedpriv = 0;extern PRLock *_pr_sleeplock;static struct _PT_Bookeeping{ PRLock *ml; /* a lock to protect ourselves */ PRCondVar *cv; /* used to signal global things */ PRInt32 system, user; /* a count of the two different types */ PRUintn this_many; /* number of threads allowed for exit */ pthread_key_t key; /* private private data key */ PRThread *first, *last; /* list of threads we know about */#if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING) PRInt32 minPrio, maxPrio; /* range of scheduling priorities */#endif} pt_book = {0};static void _pt_thread_death(void *arg);static void init_pthread_gc_support(void);#if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING)static PRIntn pt_PriorityMap(PRThreadPriority pri){#ifdef NTO /* This priority algorithm causes lots of problems on Neutrino * for now I have just hard coded everything to run at priority 10 * until I can come up with a new algorithm. * Jerry.Kirk@Nexwarecorp.com */ return 10;#else return pt_book.minPrio + pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST;#endif}#endif#if defined(GC_LEAK_DETECTOR) && (__GLIBC__ >= 2) && defined(__i386__) #include <setjmp.h>typedef struct stack_frame stack_frame;struct stack_frame { stack_frame* next; void* pc;};static stack_frame* GetStackFrame(){ jmp_buf jb; stack_frame* currentFrame; setjmp(jb); currentFrame = (stack_frame*)(jb[0].__jmpbuf[JB_BP]); currentFrame = currentFrame->next; return currentFrame;}static void* GetStackTop(){ stack_frame* frame; frame = GetStackFrame(); while (frame != NULL) { ptrdiff_t pc = (ptrdiff_t)frame->pc; if ((pc < 0x08000000) || (pc > 0x7fffffff) || (frame->next < frame)) return frame; frame = frame->next; } return NULL;}#endif /* GC_LEAK_DETECTOR && (__GLIBC__ >= 2) && __i386__ *//*** Initialize a stack for a native pthread thread*/static void _PR_InitializeStack(PRThreadStack *ts){ if( ts && (ts->stackTop == 0) ) { ts->allocBase = (char *) &ts; ts->allocSize = ts->stackSize; /* ** Setup stackTop and stackBottom values. */#ifdef HAVE_STACK_GROWING_UP ts->stackBottom = ts->allocBase + ts->stackSize; ts->stackTop = ts->allocBase;#else#ifdef GC_LEAK_DETECTOR ts->stackTop = GetStackTop(); ts->stackBottom = ts->stackTop - ts->stackSize;#else ts->stackTop = ts->allocBase; ts->stackBottom = ts->allocBase - ts->stackSize;#endif#endif }}static void *_pt_root(void *arg){ PRIntn rv; PRThread *thred = (PRThread*)arg; PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE; /* * Both the parent thread and this new thread set thred->id. * The new thread must ensure that thred->id is set before * it executes its startFunc. The parent thread must ensure * that thred->id is set before PR_CreateThread() returns. * Both threads set thred->id without holding a lock. Since * they are writing the same value, this unprotected double * write should be safe. */ thred->id = pthread_self(); /* ** DCE Threads can't detach during creation, so do it late. ** I would like to do it only here, but that doesn't seem ** to work. */#if defined(_PR_DCETHREADS) if (detached) { /* pthread_detach() modifies its argument, so we must pass a copy */ pthread_t self = thred->id; rv = pthread_detach(&self); PR_ASSERT(0 == rv); }#endif /* defined(_PR_DCETHREADS) */ /* Set up the thread stack information */ _PR_InitializeStack(thred->stack); /* * Set within the current thread the pointer to our object. * This object will be deleted when the thread termintates, * whether in a join or detached (see _PR_InitThreads()). */ rv = pthread_setspecific(pt_book.key, thred); PR_ASSERT(0 == rv); /* make the thread visible to the rest of the runtime */ PR_Lock(pt_book.ml); /* If this is a GCABLE thread, set its state appropriately */ if (thred->suspend & PT_THREAD_SETGCABLE) thred->state |= PT_THREAD_GCABLE; thred->suspend = 0; thred->prev = pt_book.last; pt_book.last->next = thred; thred->next = NULL; pt_book.last = thred; PR_Unlock(pt_book.ml); thred->startFunc(thred->arg); /* make visible to the client */ /* unhook the thread from the runtime */ PR_Lock(pt_book.ml); /* * At this moment, PR_CreateThread() may not have set thred->id yet. * It is safe for a detached thread to free thred only after * PR_CreateThread() has set thred->id. */ if (detached) { while (!thred->okToDelete) PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); } if (thred->state & PT_THREAD_SYSTEM) pt_book.system -= 1; else if (--pt_book.user == pt_book.this_many) PR_NotifyAllCondVar(pt_book.cv); thred->prev->next = thred->next; if (NULL == thred->next) pt_book.last = thred->prev; else thred->next->prev = thred->prev; PR_Unlock(pt_book.ml); /* * Here we set the pthread's backpointer to the PRThread to NULL. * Otherwise the desctructor would get called eagerly as the thread * returns to the pthread runtime. The joining thread would them be * the proud possessor of a dangling reference. However, this is the * last chance to delete the object if the thread is detached, so * just let the destuctor do the work. */ if (PR_FALSE == detached) { rv = pthread_setspecific(pt_book.key, NULL); PR_ASSERT(0 == rv); } return NULL;} /* _pt_root */static PRThread* pt_AttachThread(void){ PRThread *thred = NULL; /* * NSPR must have been initialized when PR_AttachThread is called. * We cannot have PR_AttachThread call implicit initialization * because if multiple threads call PR_AttachThread simultaneously, * NSPR may be initialized more than once. * We can't call any function that calls PR_GetCurrentThread() * either (e.g., PR_SetError()) as that will result in infinite * recursion. */ if (!_pr_initialized) return NULL; /* PR_NEWZAP must not call PR_GetCurrentThread() */ thred = PR_NEWZAP(PRThread); if (NULL != thred) { int rv; thred->priority = PR_PRIORITY_NORMAL; thred->id = pthread_self(); rv = pthread_setspecific(pt_book.key, thred); PR_ASSERT(0 == rv); thred->state = PT_THREAD_GLOBAL | PT_THREAD_FOREIGN; PR_Lock(pt_book.ml); /* then put it into the list */ thred->prev = pt_book.last; pt_book.last->next = thred; thred->next = NULL; pt_book.last = thred; PR_Unlock(pt_book.ml); } return thred; /* may be NULL */} /* pt_AttachThread */static PRThread* _PR_CreateThread( PRThreadType type, void (*start)(void *arg), void *arg, PRThreadPriority priority, PRThreadScope scope, PRThreadState state, PRUint32 stackSize, PRBool isGCAble){ int rv; PRThread *thred; pthread_attr_t tattr; if (!_pr_initialized) _PR_ImplicitInitialization(); if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority) priority = PR_PRIORITY_FIRST; else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority) priority = PR_PRIORITY_LAST; rv = _PT_PTHREAD_ATTR_INIT(&tattr); PR_ASSERT(0 == rv); if (EPERM != pt_schedpriv) {#if !defined(_PR_DCETHREADS) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) struct sched_param schedule;#endif#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) rv = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED); PR_ASSERT(0 == rv);#endif /* Use the default scheduling policy */#if defined(_PR_DCETHREADS) rv = pthread_attr_setprio(&tattr, pt_PriorityMap(priority)); PR_ASSERT(0 == rv);#elif defined(_POSIX_THREAD_PRIORITY_SCHEDULING) rv = pthread_attr_getschedparam(&tattr, &schedule); PR_ASSERT(0 == rv); schedule.sched_priority = pt_PriorityMap(priority); rv = pthread_attr_setschedparam(&tattr, &schedule); PR_ASSERT(0 == rv);#ifdef NTO rv = pthread_attr_setschedpolicy(&tattr, SCHED_RR); /* Round Robin */ PR_ASSERT(0 == rv);#endif#endif /* !defined(_PR_DCETHREADS) */ } /* * DCE threads can't set detach state before creating the thread. * AIX can't set detach late. Why can't we all just get along? */#if !defined(_PR_DCETHREADS) rv = pthread_attr_setdetachstate(&tattr, ((PR_JOINABLE_THREAD == state) ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED)); PR_ASSERT(0 == rv);#endif /* !defined(_PR_DCETHREADS) */ if (0 == stackSize) stackSize = (64 * 1024); /* default == 64K */#ifdef _MD_MINIMUM_STACK_SIZE if (stackSize < _MD_MINIMUM_STACK_SIZE) stackSize = _MD_MINIMUM_STACK_SIZE;#endif /* * Linux doesn't have pthread_attr_setstacksize. */#ifndef LINUX rv = pthread_attr_setstacksize(&tattr, stackSize); PR_ASSERT(0 == rv);#endif thred = PR_NEWZAP(PRThread); if (NULL == thred) { PR_SetError(PR_OUT_OF_MEMORY_ERROR, errno); goto done; } else { pthread_t id; thred->arg = arg; thred->startFunc = start; thred->priority = priority; if (PR_UNJOINABLE_THREAD == state) thred->state |= PT_THREAD_DETACHED; if (PR_LOCAL_THREAD == scope) scope = PR_GLOBAL_THREAD; if (PR_GLOBAL_BOUND_THREAD == scope) {#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM); if (rv) { /* * system scope not supported */ scope = PR_GLOBAL_THREAD; /* * reset scope */ rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS); PR_ASSERT(0 == rv); }#endif } if (PR_GLOBAL_THREAD == scope) thred->state |= PT_THREAD_GLOBAL; else if (PR_GLOBAL_BOUND_THREAD == scope) thred->state |= (PT_THREAD_GLOBAL | PT_THREAD_BOUND); else /* force it global */ thred->state |= PT_THREAD_GLOBAL; if (PR_SYSTEM_THREAD == type) thred->state |= PT_THREAD_SYSTEM; thred->suspend =(isGCAble) ? PT_THREAD_SETGCABLE : 0; thred->stack = PR_NEWZAP(PRThreadStack); if (thred->stack == NULL) { PRIntn oserr = errno; PR_Free(thred); /* all that work ... poof! */ PR_SetError(PR_OUT_OF_MEMORY_ERROR, oserr); thred = NULL; /* and for what? */ goto done; } thred->stack->stackSize = stackSize; thred->stack->thr = thred;#ifdef PT_NO_SIGTIMEDWAIT pthread_mutex_init(&thred->suspendResumeMutex,NULL); pthread_cond_init(&thred->suspendResumeCV,NULL);#endif /* make the thread counted to the rest of the runtime */ PR_Lock(pt_book.ml); if (PR_SYSTEM_THREAD == type) pt_book.system += 1; else pt_book.user += 1; PR_Unlock(pt_book.ml); /* * We pass a pointer to a local copy (instead of thred->id) * to pthread_create() because who knows what wacky things * pthread_create() may be doing to its argument. */ rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);#if !defined(_PR_DCETHREADS) if (EPERM == rv) {#if defined(IRIX) if (PR_GLOBAL_BOUND_THREAD == scope) { /* * SCOPE_SYSTEM requires appropriate privilege * reset to process scope and try again */ rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS); PR_ASSERT(0 == rv); thred->state &= ~PT_THREAD_BOUND; }#else /* Remember that we don't have thread scheduling privilege. */ pt_schedpriv = EPERM; PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("_PR_CreateThread: no thread scheduling privilege")); /* Try creating the thread again without setting priority. */#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) rv = pthread_attr_setinheritsched(&tattr, PTHREAD_INHERIT_SCHED); PR_ASSERT(0 == rv);#endif#endif /* IRIX */ rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred); }#endif if (0 != rv) {#if defined(_PR_DCETHREADS) PRIntn oserr = errno;#else PRIntn oserr = rv;#endif PR_Lock(pt_book.ml); if (thred->state & PT_THREAD_SYSTEM) pt_book.system -= 1; else if (--pt_book.user == pt_book.this_many) PR_NotifyAllCondVar(pt_book.cv); PR_Unlock(pt_book.ml); PR_Free(thred->stack); PR_Free(thred); /* all that work ... poof! */ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, oserr); thred = NULL; /* and for what? */ goto done; } /* * Both the parent thread and this new thread set thred->id. * The parent thread must ensure that thred->id is set before * PR_CreateThread() returns. (See comments in _pt_root().) */ thred->id = id; /* * If the new thread is detached, tell it that PR_CreateThread() * has set thred->id so it's ok to delete thred. */ if (PR_UNJOINABLE_THREAD == state) { PR_Lock(pt_book.ml); thred->okToDelete = PR_TRUE; PR_NotifyAllCondVar(pt_book.cv); PR_Unlock(pt_book.ml); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -