andre@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: /* andre@0: ** File: ptthread.c andre@0: ** Descritpion: Implemenation for threds using pthreds andre@0: ** Exports: ptthread.h andre@0: */ andre@0: andre@0: #if defined(_PR_PTHREADS) || defined(_PR_DCETHREADS) andre@0: andre@0: #include "prlog.h" andre@0: #include "primpl.h" andre@0: #include "prpdce.h" andre@0: andre@0: #include andre@0: #include andre@0: #include andre@0: #include andre@0: #include andre@0: andre@0: #ifdef SYMBIAN andre@0: /* In Open C sched_get_priority_min/max do not work properly, so we undefine andre@0: * _POSIX_THREAD_PRIORITY_SCHEDULING here. andre@0: */ andre@0: #undef _POSIX_THREAD_PRIORITY_SCHEDULING andre@0: #endif andre@0: andre@0: #ifdef _PR_NICE_PRIORITY_SCHEDULING andre@0: #undef _POSIX_THREAD_PRIORITY_SCHEDULING andre@0: #include andre@0: #ifndef HAVE_GETTID andre@0: #define gettid() (syscall(SYS_gettid)) andre@0: #endif andre@0: #endif andre@0: andre@0: /* andre@0: * Record whether or not we have the privilege to set the scheduling andre@0: * policy and priority of threads. 0 means that privilege is available. andre@0: * EPERM means that privilege is not available. andre@0: */ andre@0: andre@0: static PRIntn pt_schedpriv = 0; andre@0: extern PRLock *_pr_sleeplock; andre@0: andre@0: static struct _PT_Bookeeping andre@0: { andre@0: PRLock *ml; /* a lock to protect ourselves */ andre@0: PRCondVar *cv; /* used to signal global things */ andre@0: PRInt32 system, user; /* a count of the two different types */ andre@0: PRUintn this_many; /* number of threads allowed for exit */ andre@0: pthread_key_t key; /* thread private data key */ andre@0: PRBool keyCreated; /* whether 'key' should be deleted */ andre@0: PRThread *first, *last; /* list of threads we know about */ andre@0: #if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING) andre@0: PRInt32 minPrio, maxPrio; /* range of scheduling priorities */ andre@0: #endif andre@0: } pt_book = {0}; andre@0: andre@0: static void _pt_thread_death(void *arg); andre@0: static void _pt_thread_death_internal(void *arg, PRBool callDestructors); andre@0: static void init_pthread_gc_support(void); andre@0: andre@0: #if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING) andre@0: static PRIntn pt_PriorityMap(PRThreadPriority pri) andre@0: { andre@0: #ifdef NTO andre@0: /* This priority algorithm causes lots of problems on Neutrino andre@0: * for now I have just hard coded everything to run at priority 10 andre@0: * until I can come up with a new algorithm. andre@0: * Jerry.Kirk@Nexwarecorp.com andre@0: */ andre@0: return 10; andre@0: #else andre@0: return pt_book.minPrio + andre@0: pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST; andre@0: #endif andre@0: } andre@0: #elif defined(_PR_NICE_PRIORITY_SCHEDULING) andre@0: /* andre@0: * This functions maps higher priorities to lower nice values relative to the andre@0: * nice value specified in the |nice| parameter. The corresponding relative andre@0: * adjustments are: andre@0: * andre@0: * PR_PRIORITY_LOW +1 andre@0: * PR_PRIORITY_NORMAL 0 andre@0: * PR_PRIORITY_HIGH -1 andre@0: * PR_PRIORITY_URGENT -2 andre@0: */ andre@0: static int pt_RelativePriority(int nice, PRThreadPriority pri) andre@0: { andre@0: return nice + (1 - pri); andre@0: } andre@0: #endif andre@0: andre@0: /* andre@0: ** Initialize a stack for a native pthread thread andre@0: */ andre@0: static void _PR_InitializeStack(PRThreadStack *ts) andre@0: { andre@0: if( ts && (ts->stackTop == 0) ) { andre@0: ts->allocBase = (char *) &ts; andre@0: ts->allocSize = ts->stackSize; andre@0: andre@0: /* andre@0: ** Setup stackTop and stackBottom values. andre@0: */ andre@0: #ifdef HAVE_STACK_GROWING_UP andre@0: ts->stackBottom = ts->allocBase + ts->stackSize; andre@0: ts->stackTop = ts->allocBase; andre@0: #else andre@0: ts->stackTop = ts->allocBase; andre@0: ts->stackBottom = ts->allocBase - ts->stackSize; andre@0: #endif andre@0: } andre@0: } andre@0: andre@0: static void *_pt_root(void *arg) andre@0: { andre@0: PRIntn rv; andre@0: PRThread *thred = (PRThread*)arg; andre@0: PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE; andre@0: pthread_t id = pthread_self(); andre@0: #ifdef _PR_NICE_PRIORITY_SCHEDULING andre@0: pid_t tid; andre@0: #endif andre@0: andre@0: #ifdef _PR_NICE_PRIORITY_SCHEDULING andre@0: /* andre@0: * We need to know the kernel thread ID of each thread in order to andre@0: * set its nice value hence we do it here instead of at creation time. andre@0: */ andre@0: tid = gettid(); andre@0: errno = 0; andre@0: rv = getpriority(PRIO_PROCESS, 0); andre@0: andre@0: /* If we cannot read the main thread's nice value don't try to change the andre@0: * new thread's nice value. */ andre@0: if (errno == 0) { andre@0: setpriority(PRIO_PROCESS, tid, andre@0: pt_RelativePriority(rv, thred->priority)); andre@0: } andre@0: #endif andre@0: andre@0: /* andre@0: ** DCE Threads can't detach during creation, so do it late. andre@0: ** I would like to do it only here, but that doesn't seem andre@0: ** to work. andre@0: */ andre@0: #if defined(_PR_DCETHREADS) andre@0: if (detached) andre@0: { andre@0: /* pthread_detach() modifies its argument, so we must pass a copy */ andre@0: pthread_t self = id; andre@0: rv = pthread_detach(&self); andre@0: PR_ASSERT(0 == rv); andre@0: } andre@0: #endif /* defined(_PR_DCETHREADS) */ andre@0: andre@0: /* Set up the thread stack information */ andre@0: _PR_InitializeStack(thred->stack); andre@0: andre@0: /* andre@0: * Set within the current thread the pointer to our object. andre@0: * This object will be deleted when the thread termintates, andre@0: * whether in a join or detached (see _PR_InitThreads()). andre@0: */ andre@0: rv = pthread_setspecific(pt_book.key, thred); andre@0: PR_ASSERT(0 == rv); andre@0: andre@0: /* make the thread visible to the rest of the runtime */ andre@0: PR_Lock(pt_book.ml); andre@0: /* andre@0: * Both the parent thread and this new thread set thred->id. andre@0: * The new thread must ensure that thred->id is set before andre@0: * it executes its startFunc. The parent thread must ensure andre@0: * that thred->id is set before PR_CreateThread() returns. andre@0: * Both threads set thred->id while holding pt_book.ml and andre@0: * use thred->idSet to ensure thred->id is written only once. andre@0: */ andre@0: if (!thred->idSet) andre@0: { andre@0: thred->id = id; andre@0: thred->idSet = PR_TRUE; andre@0: } andre@0: else andre@0: { andre@0: PR_ASSERT(pthread_equal(thred->id, id)); andre@0: } andre@0: andre@0: #ifdef _PR_NICE_PRIORITY_SCHEDULING andre@0: thred->tid = tid; andre@0: PR_NotifyAllCondVar(pt_book.cv); andre@0: #endif andre@0: andre@0: /* If this is a GCABLE thread, set its state appropriately */ andre@0: if (thred->suspend & PT_THREAD_SETGCABLE) andre@0: thred->state |= PT_THREAD_GCABLE; andre@0: thred->suspend = 0; andre@0: andre@0: thred->prev = pt_book.last; andre@0: if (pt_book.last) andre@0: pt_book.last->next = thred; andre@0: else andre@0: pt_book.first = thred; andre@0: thred->next = NULL; andre@0: pt_book.last = thred; andre@0: PR_Unlock(pt_book.ml); andre@0: andre@0: thred->startFunc(thred->arg); /* make visible to the client */ andre@0: andre@0: /* unhook the thread from the runtime */ andre@0: PR_Lock(pt_book.ml); andre@0: /* andre@0: * At this moment, PR_CreateThread() may not have set thred->id yet. andre@0: * It is safe for a detached thread to free thred only after andre@0: * PR_CreateThread() has accessed thred->id and thred->idSet. andre@0: */ andre@0: if (detached) andre@0: { andre@0: while (!thred->okToDelete) andre@0: PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); andre@0: } andre@0: andre@0: if (thred->state & PT_THREAD_SYSTEM) andre@0: pt_book.system -= 1; andre@0: else if (--pt_book.user == pt_book.this_many) andre@0: PR_NotifyAllCondVar(pt_book.cv); andre@0: if (NULL == thred->prev) andre@0: pt_book.first = thred->next; andre@0: else andre@0: thred->prev->next = thred->next; andre@0: if (NULL == thred->next) andre@0: pt_book.last = thred->prev; andre@0: else andre@0: thred->next->prev = thred->prev; andre@0: PR_Unlock(pt_book.ml); andre@0: andre@0: /* andre@0: * Here we set the pthread's backpointer to the PRThread to NULL. andre@0: * Otherwise the destructor would get called eagerly as the thread andre@0: * returns to the pthread runtime. The joining thread would them be andre@0: * the proud possessor of a dangling reference. However, this is the andre@0: * last chance to delete the object if the thread is detached, so andre@0: * just let the destructor do the work. andre@0: */ andre@0: if (PR_FALSE == detached) andre@0: { andre@0: /* Call TPD destructors on this thread. */ andre@0: _PR_DestroyThreadPrivate(thred); andre@0: rv = pthread_setspecific(pt_book.key, NULL); andre@0: PR_ASSERT(0 == rv); andre@0: } andre@0: andre@0: return NULL; andre@0: } /* _pt_root */ andre@0: andre@0: static PRThread* pt_AttachThread(void) andre@0: { andre@0: PRThread *thred = NULL; andre@0: andre@0: /* andre@0: * NSPR must have been initialized when PR_AttachThread is called. andre@0: * We cannot have PR_AttachThread call implicit initialization andre@0: * because if multiple threads call PR_AttachThread simultaneously, andre@0: * NSPR may be initialized more than once. andre@0: * We can't call any function that calls PR_GetCurrentThread() andre@0: * either (e.g., PR_SetError()) as that will result in infinite andre@0: * recursion. andre@0: */ andre@0: if (!_pr_initialized) return NULL; andre@0: andre@0: /* PR_NEWZAP must not call PR_GetCurrentThread() */ andre@0: thred = PR_NEWZAP(PRThread); andre@0: if (NULL != thred) andre@0: { andre@0: int rv; andre@0: andre@0: thred->priority = PR_PRIORITY_NORMAL; andre@0: thred->id = pthread_self(); andre@0: thred->idSet = PR_TRUE; andre@0: #ifdef _PR_NICE_PRIORITY_SCHEDULING andre@0: thred->tid = gettid(); andre@0: #endif andre@0: rv = pthread_setspecific(pt_book.key, thred); andre@0: PR_ASSERT(0 == rv); andre@0: andre@0: thred->state = PT_THREAD_GLOBAL | PT_THREAD_FOREIGN; andre@0: PR_Lock(pt_book.ml); andre@0: andre@0: /* then put it into the list */ andre@0: thred->prev = pt_book.last; andre@0: if (pt_book.last) andre@0: pt_book.last->next = thred; andre@0: else andre@0: pt_book.first = thred; andre@0: thred->next = NULL; andre@0: pt_book.last = thred; andre@0: PR_Unlock(pt_book.ml); andre@0: andre@0: } andre@0: return thred; /* may be NULL */ andre@0: } /* pt_AttachThread */ andre@0: andre@0: static PRThread* _PR_CreateThread( andre@0: PRThreadType type, void (*start)(void *arg), andre@0: void *arg, PRThreadPriority priority, PRThreadScope scope, andre@0: PRThreadState state, PRUint32 stackSize, PRBool isGCAble) andre@0: { andre@0: int rv; andre@0: PRThread *thred; andre@0: pthread_attr_t tattr; andre@0: andre@0: if (!_pr_initialized) _PR_ImplicitInitialization(); andre@0: andre@0: if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority) andre@0: priority = PR_PRIORITY_FIRST; andre@0: else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority) andre@0: priority = PR_PRIORITY_LAST; andre@0: andre@0: rv = _PT_PTHREAD_ATTR_INIT(&tattr); andre@0: PR_ASSERT(0 == rv); andre@0: andre@0: if (EPERM != pt_schedpriv) andre@0: { andre@0: #if !defined(_PR_DCETHREADS) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) andre@0: struct sched_param schedule; andre@0: #endif andre@0: andre@0: #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) andre@0: rv = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED); andre@0: PR_ASSERT(0 == rv); andre@0: #endif andre@0: andre@0: /* Use the default scheduling policy */ andre@0: andre@0: #if defined(_PR_DCETHREADS) andre@0: rv = pthread_attr_setprio(&tattr, pt_PriorityMap(priority)); andre@0: PR_ASSERT(0 == rv); andre@0: #elif defined(_POSIX_THREAD_PRIORITY_SCHEDULING) andre@0: rv = pthread_attr_getschedparam(&tattr, &schedule); andre@0: PR_ASSERT(0 == rv); andre@0: schedule.sched_priority = pt_PriorityMap(priority); andre@0: rv = pthread_attr_setschedparam(&tattr, &schedule); andre@0: PR_ASSERT(0 == rv); andre@0: #ifdef NTO andre@0: rv = pthread_attr_setschedpolicy(&tattr, SCHED_RR); /* Round Robin */ andre@0: PR_ASSERT(0 == rv); andre@0: #endif andre@0: #endif /* !defined(_PR_DCETHREADS) */ andre@0: } andre@0: andre@0: /* andre@0: * DCE threads can't set detach state before creating the thread. andre@0: * AIX can't set detach late. Why can't we all just get along? andre@0: */ andre@0: #if !defined(_PR_DCETHREADS) andre@0: rv = pthread_attr_setdetachstate(&tattr, andre@0: ((PR_JOINABLE_THREAD == state) ? andre@0: PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED)); andre@0: PR_ASSERT(0 == rv); andre@0: #endif /* !defined(_PR_DCETHREADS) */ andre@0: andre@0: /* andre@0: * If stackSize is 0, we use the default pthread stack size. andre@0: */ andre@0: if (stackSize) andre@0: { andre@0: #ifdef _MD_MINIMUM_STACK_SIZE andre@0: if (stackSize < _MD_MINIMUM_STACK_SIZE) andre@0: stackSize = _MD_MINIMUM_STACK_SIZE; andre@0: #endif andre@0: rv = pthread_attr_setstacksize(&tattr, stackSize); andre@0: PR_ASSERT(0 == rv); andre@0: } andre@0: andre@0: thred = PR_NEWZAP(PRThread); andre@0: if (NULL == thred) andre@0: { andre@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, errno); andre@0: goto done; andre@0: } andre@0: else andre@0: { andre@0: pthread_t id; andre@0: andre@0: thred->arg = arg; andre@0: thred->startFunc = start; andre@0: thred->priority = priority; andre@0: if (PR_UNJOINABLE_THREAD == state) andre@0: thred->state |= PT_THREAD_DETACHED; andre@0: andre@0: if (PR_LOCAL_THREAD == scope) andre@0: scope = PR_GLOBAL_THREAD; andre@0: andre@0: if (PR_GLOBAL_BOUND_THREAD == scope) { andre@0: #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) andre@0: rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM); andre@0: if (rv) { andre@0: /* andre@0: * system scope not supported andre@0: */ andre@0: scope = PR_GLOBAL_THREAD; andre@0: /* andre@0: * reset scope andre@0: */ andre@0: rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS); andre@0: PR_ASSERT(0 == rv); andre@0: } andre@0: #endif andre@0: } andre@0: if (PR_GLOBAL_THREAD == scope) andre@0: thred->state |= PT_THREAD_GLOBAL; andre@0: else if (PR_GLOBAL_BOUND_THREAD == scope) andre@0: thred->state |= (PT_THREAD_GLOBAL | PT_THREAD_BOUND); andre@0: else /* force it global */ andre@0: thred->state |= PT_THREAD_GLOBAL; andre@0: if (PR_SYSTEM_THREAD == type) andre@0: thred->state |= PT_THREAD_SYSTEM; andre@0: andre@0: thred->suspend =(isGCAble) ? PT_THREAD_SETGCABLE : 0; andre@0: andre@0: thred->stack = PR_NEWZAP(PRThreadStack); andre@0: if (thred->stack == NULL) { andre@0: PRIntn oserr = errno; andre@0: PR_Free(thred); /* all that work ... poof! */ andre@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, oserr); andre@0: thred = NULL; /* and for what? */ andre@0: goto done; andre@0: } andre@0: thred->stack->stackSize = stackSize; andre@0: thred->stack->thr = thred; andre@0: andre@0: #ifdef PT_NO_SIGTIMEDWAIT andre@0: pthread_mutex_init(&thred->suspendResumeMutex,NULL); andre@0: pthread_cond_init(&thred->suspendResumeCV,NULL); andre@0: #endif andre@0: andre@0: /* make the thread counted to the rest of the runtime */ andre@0: PR_Lock(pt_book.ml); andre@0: if (PR_SYSTEM_THREAD == type) andre@0: pt_book.system += 1; andre@0: else pt_book.user += 1; andre@0: PR_Unlock(pt_book.ml); andre@0: andre@0: /* andre@0: * We pass a pointer to a local copy (instead of thred->id) andre@0: * to pthread_create() because who knows what wacky things andre@0: * pthread_create() may be doing to its argument. andre@0: */ andre@0: rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred); andre@0: andre@0: #if !defined(_PR_DCETHREADS) andre@0: if (EPERM == rv) andre@0: { andre@0: #if defined(IRIX) andre@0: if (PR_GLOBAL_BOUND_THREAD == scope) { andre@0: /* andre@0: * SCOPE_SYSTEM requires appropriate privilege andre@0: * reset to process scope and try again andre@0: */ andre@0: rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS); andre@0: PR_ASSERT(0 == rv); andre@0: thred->state &= ~PT_THREAD_BOUND; andre@0: } andre@0: #else andre@0: /* Remember that we don't have thread scheduling privilege. */ andre@0: pt_schedpriv = EPERM; andre@0: PR_LOG(_pr_thread_lm, PR_LOG_MIN, andre@0: ("_PR_CreateThread: no thread scheduling privilege")); andre@0: /* Try creating the thread again without setting priority. */ andre@0: #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) andre@0: rv = pthread_attr_setinheritsched(&tattr, PTHREAD_INHERIT_SCHED); andre@0: PR_ASSERT(0 == rv); andre@0: #endif andre@0: #endif /* IRIX */ andre@0: rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred); andre@0: } andre@0: #endif andre@0: andre@0: if (0 != rv) andre@0: { andre@0: #if defined(_PR_DCETHREADS) andre@0: PRIntn oserr = errno; andre@0: #else andre@0: PRIntn oserr = rv; andre@0: #endif andre@0: PR_Lock(pt_book.ml); andre@0: if (thred->state & PT_THREAD_SYSTEM) andre@0: pt_book.system -= 1; andre@0: else if (--pt_book.user == pt_book.this_many) andre@0: PR_NotifyAllCondVar(pt_book.cv); andre@0: PR_Unlock(pt_book.ml); andre@0: andre@0: PR_Free(thred->stack); andre@0: PR_Free(thred); /* all that work ... poof! */ andre@0: PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, oserr); andre@0: thred = NULL; /* and for what? */ andre@0: goto done; andre@0: } andre@0: andre@0: PR_Lock(pt_book.ml); andre@0: /* andre@0: * Both the parent thread and this new thread set thred->id. andre@0: * The parent thread must ensure that thred->id is set before andre@0: * PR_CreateThread() returns. (See comments in _pt_root().) andre@0: */ andre@0: if (!thred->idSet) andre@0: { andre@0: thred->id = id; andre@0: thred->idSet = PR_TRUE; andre@0: } andre@0: else andre@0: { andre@0: PR_ASSERT(pthread_equal(thred->id, id)); andre@0: } andre@0: andre@0: /* andre@0: * If the new thread is detached, tell it that PR_CreateThread() has andre@0: * accessed thred->id and thred->idSet so it's ok to delete thred. andre@0: */ andre@0: if (PR_UNJOINABLE_THREAD == state) andre@0: { andre@0: thred->okToDelete = PR_TRUE; andre@0: PR_NotifyAllCondVar(pt_book.cv); andre@0: } andre@0: PR_Unlock(pt_book.ml); andre@0: } andre@0: andre@0: done: andre@0: rv = _PT_PTHREAD_ATTR_DESTROY(&tattr); andre@0: PR_ASSERT(0 == rv); andre@0: andre@0: return thred; andre@0: } /* _PR_CreateThread */ andre@0: andre@0: PR_IMPLEMENT(PRThread*) PR_CreateThread( andre@0: PRThreadType type, void (*start)(void *arg), void *arg, andre@0: PRThreadPriority priority, PRThreadScope scope, andre@0: PRThreadState state, PRUint32 stackSize) andre@0: { andre@0: return _PR_CreateThread( andre@0: type, start, arg, priority, scope, state, stackSize, PR_FALSE); andre@0: } /* PR_CreateThread */ andre@0: andre@0: PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble( andre@0: PRThreadType type, void (*start)(void *arg), void *arg, andre@0: PRThreadPriority priority, PRThreadScope scope, andre@0: PRThreadState state, PRUint32 stackSize) andre@0: { andre@0: return _PR_CreateThread( andre@0: type, start, arg, priority, scope, state, stackSize, PR_TRUE); andre@0: } /* PR_CreateThreadGCAble */ andre@0: andre@0: PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thred) andre@0: { andre@0: return thred->environment; andre@0: } /* GetExecutionEnvironment */ andre@0: andre@0: PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thred, void *env) andre@0: { andre@0: thred->environment = env; andre@0: } /* SetExecutionEnvironment */ andre@0: andre@0: PR_IMPLEMENT(PRThread*) PR_AttachThread( andre@0: PRThreadType type, PRThreadPriority priority, PRThreadStack *stack) andre@0: { andre@0: return PR_GetCurrentThread(); andre@0: } /* PR_AttachThread */ andre@0: andre@0: andre@0: PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thred) andre@0: { andre@0: int rv = -1; andre@0: void *result = NULL; andre@0: PR_ASSERT(thred != NULL); andre@0: andre@0: if ((0xafafafaf == thred->state) andre@0: || (PT_THREAD_DETACHED == (PT_THREAD_DETACHED & thred->state)) andre@0: || (PT_THREAD_FOREIGN == (PT_THREAD_FOREIGN & thred->state))) andre@0: { andre@0: /* andre@0: * This might be a bad address, but if it isn't, the state should andre@0: * either be an unjoinable thread or it's already had the object andre@0: * deleted. However, the client that called join on a detached andre@0: * thread deserves all the rath I can muster.... andre@0: */ andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: PR_LogPrint( andre@0: "PR_JoinThread: %p not joinable | already smashed\n", thred); andre@0: } andre@0: else andre@0: { andre@0: pthread_t id = thred->id; andre@0: rv = pthread_join(id, &result); andre@0: PR_ASSERT(rv == 0 && result == NULL); andre@0: if (0 == rv) andre@0: { andre@0: #ifdef _PR_DCETHREADS andre@0: rv = pthread_detach(&id); andre@0: PR_ASSERT(0 == rv); andre@0: #endif andre@0: /* andre@0: * PR_FALSE, because the thread already called the TPD andre@0: * destructors before exiting _pt_root. andre@0: */ andre@0: _pt_thread_death_internal(thred, PR_FALSE); andre@0: } andre@0: else andre@0: { andre@0: PRErrorCode prerror; andre@0: switch (rv) andre@0: { andre@0: case EINVAL: /* not a joinable thread */ andre@0: case ESRCH: /* no thread with given ID */ andre@0: prerror = PR_INVALID_ARGUMENT_ERROR; andre@0: break; andre@0: case EDEADLK: /* a thread joining with itself */ andre@0: prerror = PR_DEADLOCK_ERROR; andre@0: break; andre@0: default: andre@0: prerror = PR_UNKNOWN_ERROR; andre@0: break; andre@0: } andre@0: PR_SetError(prerror, rv); andre@0: } andre@0: } andre@0: return (0 == rv) ? PR_SUCCESS : PR_FAILURE; andre@0: } /* PR_JoinThread */ andre@0: andre@0: PR_IMPLEMENT(void) PR_DetachThread(void) andre@0: { andre@0: void *thred; andre@0: int rv; andre@0: andre@0: _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred); andre@0: if (NULL == thred) return; andre@0: _pt_thread_death(thred); andre@0: rv = pthread_setspecific(pt_book.key, NULL); andre@0: PR_ASSERT(0 == rv); andre@0: } /* PR_DetachThread */ andre@0: andre@0: PR_IMPLEMENT(PRThread*) PR_GetCurrentThread(void) andre@0: { andre@0: void *thred; andre@0: andre@0: if (!_pr_initialized) _PR_ImplicitInitialization(); andre@0: andre@0: _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred); andre@0: if (NULL == thred) thred = pt_AttachThread(); andre@0: PR_ASSERT(NULL != thred); andre@0: return (PRThread*)thred; andre@0: } /* PR_GetCurrentThread */ andre@0: andre@0: PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thred) andre@0: { andre@0: return (thred->state & PT_THREAD_BOUND) ? andre@0: PR_GLOBAL_BOUND_THREAD : PR_GLOBAL_THREAD; andre@0: } /* PR_GetThreadScope() */ andre@0: andre@0: PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thred) andre@0: { andre@0: return (thred->state & PT_THREAD_SYSTEM) ? andre@0: PR_SYSTEM_THREAD : PR_USER_THREAD; andre@0: } andre@0: andre@0: PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thred) andre@0: { andre@0: return (thred->state & PT_THREAD_DETACHED) ? andre@0: PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD; andre@0: } /* PR_GetThreadState */ andre@0: andre@0: PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thred) andre@0: { andre@0: PR_ASSERT(thred != NULL); andre@0: return thred->priority; andre@0: } /* PR_GetThreadPriority */ andre@0: andre@0: PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred, PRThreadPriority newPri) andre@0: { andre@0: PRIntn rv = -1; andre@0: andre@0: PR_ASSERT(NULL != thred); andre@0: andre@0: if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)newPri) andre@0: newPri = PR_PRIORITY_FIRST; andre@0: else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)newPri) andre@0: newPri = PR_PRIORITY_LAST; andre@0: andre@0: #if defined(_PR_DCETHREADS) andre@0: rv = pthread_setprio(thred->id, pt_PriorityMap(newPri)); andre@0: /* pthread_setprio returns the old priority */ andre@0: #elif defined(_POSIX_THREAD_PRIORITY_SCHEDULING) andre@0: if (EPERM != pt_schedpriv) andre@0: { andre@0: int policy; andre@0: struct sched_param schedule; andre@0: andre@0: rv = pthread_getschedparam(thred->id, &policy, &schedule); andre@0: if(0 == rv) { andre@0: schedule.sched_priority = pt_PriorityMap(newPri); andre@0: rv = pthread_setschedparam(thred->id, policy, &schedule); andre@0: if (EPERM == rv) andre@0: { andre@0: pt_schedpriv = EPERM; andre@0: PR_LOG(_pr_thread_lm, PR_LOG_MIN, andre@0: ("PR_SetThreadPriority: no thread scheduling privilege")); andre@0: } andre@0: } andre@0: if (rv != 0) andre@0: rv = -1; andre@0: } andre@0: #elif defined(_PR_NICE_PRIORITY_SCHEDULING) andre@0: PR_Lock(pt_book.ml); andre@0: while (thred->tid == 0) andre@0: PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); andre@0: PR_Unlock(pt_book.ml); andre@0: andre@0: errno = 0; andre@0: rv = getpriority(PRIO_PROCESS, 0); andre@0: andre@0: /* Do not proceed unless we know the main thread's nice value. */ andre@0: if (errno == 0) { andre@0: rv = setpriority(PRIO_PROCESS, thred->tid, andre@0: pt_RelativePriority(rv, newPri)); andre@0: andre@0: if (rv == -1) andre@0: { andre@0: /* We don't set pt_schedpriv to EPERM in case errno == EPERM andre@0: * because adjusting the nice value might be permitted for certain andre@0: * ranges but not for others. */ andre@0: PR_LOG(_pr_thread_lm, PR_LOG_MIN, andre@0: ("PR_SetThreadPriority: setpriority failed with error %d", andre@0: errno)); andre@0: } andre@0: } andre@0: #endif andre@0: andre@0: thred->priority = newPri; andre@0: } /* PR_SetThreadPriority */ andre@0: andre@0: PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thred) andre@0: { andre@0: /* andre@0: ** If the target thread indicates that it's waiting, andre@0: ** find the condition and broadcast to it. Broadcast andre@0: ** since we don't know which thread (if there are more andre@0: ** than one). This sounds risky, but clients must andre@0: ** test their invariants when resumed from a wait and andre@0: ** I don't expect very many threads to be waiting on andre@0: ** a single condition and I don't expect interrupt to andre@0: ** be used very often. andre@0: ** andre@0: ** I don't know why I thought this would work. Must have andre@0: ** been one of those weaker momements after I'd been andre@0: ** smelling the vapors. andre@0: ** andre@0: ** Even with the followng changes it is possible that andre@0: ** the pointer to the condition variable is pointing andre@0: ** at a bogus value. Will the unerlying code detect andre@0: ** that? andre@0: */ andre@0: PRCondVar *cv; andre@0: PR_ASSERT(NULL != thred); andre@0: if (NULL == thred) return PR_FAILURE; andre@0: andre@0: thred->state |= PT_THREAD_ABORTED; andre@0: andre@0: cv = thred->waiting; andre@0: if ((NULL != cv) && !thred->interrupt_blocked) andre@0: { andre@0: PRIntn rv; andre@0: (void)PR_ATOMIC_INCREMENT(&cv->notify_pending); andre@0: rv = pthread_cond_broadcast(&cv->cv); andre@0: PR_ASSERT(0 == rv); andre@0: if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) andre@0: PR_DestroyCondVar(cv); andre@0: } andre@0: return PR_SUCCESS; andre@0: } /* PR_Interrupt */ andre@0: andre@0: PR_IMPLEMENT(void) PR_ClearInterrupt(void) andre@0: { andre@0: PRThread *me = PR_GetCurrentThread(); andre@0: me->state &= ~PT_THREAD_ABORTED; andre@0: } /* PR_ClearInterrupt */ andre@0: andre@0: PR_IMPLEMENT(void) PR_BlockInterrupt(void) andre@0: { andre@0: PRThread *me = PR_GetCurrentThread(); andre@0: _PT_THREAD_BLOCK_INTERRUPT(me); andre@0: } /* PR_BlockInterrupt */ andre@0: andre@0: PR_IMPLEMENT(void) PR_UnblockInterrupt(void) andre@0: { andre@0: PRThread *me = PR_GetCurrentThread(); andre@0: _PT_THREAD_UNBLOCK_INTERRUPT(me); andre@0: } /* PR_UnblockInterrupt */ andre@0: andre@0: PR_IMPLEMENT(PRStatus) PR_Yield(void) andre@0: { andre@0: static PRBool warning = PR_TRUE; andre@0: if (warning) warning = _PR_Obsolete( andre@0: "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)"); andre@0: return PR_Sleep(PR_INTERVAL_NO_WAIT); andre@0: } andre@0: andre@0: PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime ticks) andre@0: { andre@0: PRStatus rv = PR_SUCCESS; andre@0: andre@0: if (!_pr_initialized) _PR_ImplicitInitialization(); andre@0: andre@0: if (PR_INTERVAL_NO_WAIT == ticks) andre@0: { andre@0: _PT_PTHREAD_YIELD(); andre@0: } andre@0: else andre@0: { andre@0: PRCondVar *cv; andre@0: PRIntervalTime timein; andre@0: andre@0: timein = PR_IntervalNow(); andre@0: cv = PR_NewCondVar(_pr_sleeplock); andre@0: PR_ASSERT(cv != NULL); andre@0: PR_Lock(_pr_sleeplock); andre@0: do andre@0: { andre@0: PRIntervalTime now = PR_IntervalNow(); andre@0: PRIntervalTime delta = now - timein; andre@0: if (delta > ticks) break; andre@0: rv = PR_WaitCondVar(cv, ticks - delta); andre@0: } while (PR_SUCCESS == rv); andre@0: PR_Unlock(_pr_sleeplock); andre@0: PR_DestroyCondVar(cv); andre@0: } andre@0: return rv; andre@0: } /* PR_Sleep */ andre@0: andre@0: static void _pt_thread_death(void *arg) andre@0: { andre@0: void *thred; andre@0: int rv; andre@0: andre@0: _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred); andre@0: if (NULL == thred) andre@0: { andre@0: /* andre@0: * Have PR_GetCurrentThread return the expected value to the andre@0: * destructors. andre@0: */ andre@0: rv = pthread_setspecific(pt_book.key, arg); andre@0: PR_ASSERT(0 == rv); andre@0: } andre@0: andre@0: /* PR_TRUE for: call destructors */ andre@0: _pt_thread_death_internal(arg, PR_TRUE); andre@0: andre@0: if (NULL == thred) andre@0: { andre@0: rv = pthread_setspecific(pt_book.key, NULL); andre@0: PR_ASSERT(0 == rv); andre@0: } andre@0: } andre@0: andre@0: static void _pt_thread_death_internal(void *arg, PRBool callDestructors) andre@0: { andre@0: PRThread *thred = (PRThread*)arg; andre@0: andre@0: if (thred->state & (PT_THREAD_FOREIGN|PT_THREAD_PRIMORD)) andre@0: { andre@0: PR_Lock(pt_book.ml); andre@0: if (NULL == thred->prev) andre@0: pt_book.first = thred->next; andre@0: else andre@0: thred->prev->next = thred->next; andre@0: if (NULL == thred->next) andre@0: pt_book.last = thred->prev; andre@0: else andre@0: thred->next->prev = thred->prev; andre@0: PR_Unlock(pt_book.ml); andre@0: } andre@0: if (callDestructors) andre@0: _PR_DestroyThreadPrivate(thred); andre@0: PR_Free(thred->privateData); andre@0: if (NULL != thred->errorString) andre@0: PR_Free(thred->errorString); andre@0: if (NULL != thred->name) andre@0: PR_Free(thred->name); andre@0: PR_Free(thred->stack); andre@0: if (NULL != thred->syspoll_list) andre@0: PR_Free(thred->syspoll_list); andre@0: #if defined(_PR_POLL_WITH_SELECT) andre@0: if (NULL != thred->selectfd_list) andre@0: PR_Free(thred->selectfd_list); andre@0: #endif andre@0: #if defined(DEBUG) andre@0: memset(thred, 0xaf, sizeof(PRThread)); andre@0: #endif /* defined(DEBUG) */ andre@0: PR_Free(thred); andre@0: } /* _pt_thread_death */ andre@0: andre@0: void _PR_InitThreads( andre@0: PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs) andre@0: { andre@0: int rv; andre@0: PRThread *thred; andre@0: andre@0: PR_ASSERT(priority == PR_PRIORITY_NORMAL); andre@0: andre@0: #ifdef _PR_NEED_PTHREAD_INIT andre@0: /* andre@0: * On BSD/OS (3.1 and 4.0), the pthread subsystem is lazily andre@0: * initialized, but pthread_self() fails to initialize andre@0: * pthreads and hence returns a null thread ID if invoked andre@0: * by the primordial thread before any other pthread call. andre@0: * So we explicitly initialize pthreads here. andre@0: */ andre@0: pthread_init(); andre@0: #endif andre@0: andre@0: #if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING) andre@0: #if defined(FREEBSD) andre@0: { andre@0: pthread_attr_t attr; andre@0: int policy; andre@0: /* get the min and max priorities of the default policy */ andre@0: pthread_attr_init(&attr); andre@0: pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); andre@0: pthread_attr_getschedpolicy(&attr, &policy); andre@0: pt_book.minPrio = sched_get_priority_min(policy); andre@0: PR_ASSERT(-1 != pt_book.minPrio); andre@0: pt_book.maxPrio = sched_get_priority_max(policy); andre@0: PR_ASSERT(-1 != pt_book.maxPrio); andre@0: pthread_attr_destroy(&attr); andre@0: } andre@0: #else andre@0: /* andre@0: ** These might be function evaluations andre@0: */ andre@0: pt_book.minPrio = PT_PRIO_MIN; andre@0: pt_book.maxPrio = PT_PRIO_MAX; andre@0: #endif andre@0: #endif andre@0: andre@0: PR_ASSERT(NULL == pt_book.ml); andre@0: pt_book.ml = PR_NewLock(); andre@0: PR_ASSERT(NULL != pt_book.ml); andre@0: pt_book.cv = PR_NewCondVar(pt_book.ml); andre@0: PR_ASSERT(NULL != pt_book.cv); andre@0: thred = PR_NEWZAP(PRThread); andre@0: PR_ASSERT(NULL != thred); andre@0: thred->arg = NULL; andre@0: thred->startFunc = NULL; andre@0: thred->priority = priority; andre@0: thred->id = pthread_self(); andre@0: thred->idSet = PR_TRUE; andre@0: #ifdef _PR_NICE_PRIORITY_SCHEDULING andre@0: thred->tid = gettid(); andre@0: #endif andre@0: andre@0: thred->state = (PT_THREAD_DETACHED | PT_THREAD_PRIMORD); andre@0: if (PR_SYSTEM_THREAD == type) andre@0: { andre@0: thred->state |= PT_THREAD_SYSTEM; andre@0: pt_book.system += 1; andre@0: pt_book.this_many = 0; andre@0: } andre@0: else andre@0: { andre@0: pt_book.user += 1; andre@0: pt_book.this_many = 1; andre@0: } andre@0: thred->next = thred->prev = NULL; andre@0: pt_book.first = pt_book.last = thred; andre@0: andre@0: thred->stack = PR_NEWZAP(PRThreadStack); andre@0: PR_ASSERT(thred->stack != NULL); andre@0: thred->stack->stackSize = 0; andre@0: thred->stack->thr = thred; andre@0: _PR_InitializeStack(thred->stack); andre@0: andre@0: /* andre@0: * Create a key for our use to store a backpointer in the pthread andre@0: * to our PRThread object. This object gets deleted when the thread andre@0: * returns from its root in the case of a detached thread. Other andre@0: * threads delete the objects in Join. andre@0: * andre@0: * NB: The destructor logic seems to have a bug so it isn't used. andre@0: * NBB: Oh really? I'm going to give it a spin - AOF 19 June 1998. andre@0: * More info - the problem is that pthreads calls the destructor andre@0: * eagerly as the thread returns from its root, rather than lazily andre@0: * after the thread is joined. Therefore, threads that are joining andre@0: * and holding PRThread references are actually holding pointers to andre@0: * nothing. andre@0: */ andre@0: rv = _PT_PTHREAD_KEY_CREATE(&pt_book.key, _pt_thread_death); andre@0: if (0 != rv) andre@0: PR_Assert("0 == rv", __FILE__, __LINE__); andre@0: pt_book.keyCreated = PR_TRUE; andre@0: rv = pthread_setspecific(pt_book.key, thred); andre@0: PR_ASSERT(0 == rv); andre@0: } /* _PR_InitThreads */ andre@0: andre@0: #ifdef __GNUC__ andre@0: /* andre@0: * GCC supports the constructor and destructor attributes as of andre@0: * version 2.5. andre@0: */ andre@0: static void _PR_Fini(void) __attribute__ ((destructor)); andre@0: #elif defined(__SUNPRO_C) andre@0: /* andre@0: * Sun Studio compiler andre@0: */ andre@0: #pragma fini(_PR_Fini) andre@0: static void _PR_Fini(void); andre@0: #elif defined(HPUX) andre@0: /* andre@0: * Current versions of HP C compiler define __HP_cc. andre@0: * HP C compiler A.11.01.20 doesn't define __HP_cc. andre@0: */ andre@0: #if defined(__ia64) || defined(_LP64) andre@0: #pragma FINI "_PR_Fini" andre@0: static void _PR_Fini(void); andre@0: #else andre@0: /* andre@0: * Only HP-UX 10.x style initializers are supported in 32-bit links. andre@0: * Need to use the +I PR_HPUX10xInit linker option. andre@0: */ andre@0: #include andre@0: andre@0: static void _PR_Fini(void); andre@0: andre@0: void PR_HPUX10xInit(shl_t handle, int loading) andre@0: { andre@0: /* andre@0: * This function is called when a shared library is loaded as well andre@0: * as when the shared library is unloaded. Note that it may not andre@0: * be called when the user's program terminates. andre@0: * andre@0: * handle is the shl_load API handle for the shared library being andre@0: * initialized. andre@0: * andre@0: * loading is non-zero at startup and zero at termination. andre@0: */ andre@0: if (loading) { andre@0: /* ... do some initializations ... */ andre@0: } else { andre@0: _PR_Fini(); andre@0: } andre@0: } andre@0: #endif andre@0: #elif defined(AIX) andre@0: /* Need to use the -binitfini::_PR_Fini linker option. */ andre@0: #endif andre@0: andre@0: void _PR_Fini(void) andre@0: { andre@0: void *thred; andre@0: int rv; andre@0: andre@0: if (!_pr_initialized) { andre@0: /* Either NSPR was never successfully initialized or andre@0: * PR_Cleanup has been called already. */ andre@0: if (pt_book.keyCreated) andre@0: { andre@0: rv = pthread_key_delete(pt_book.key); andre@0: PR_ASSERT(0 == rv); andre@0: pt_book.keyCreated = PR_FALSE; andre@0: } andre@0: return; andre@0: } andre@0: andre@0: _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred); andre@0: if (NULL != thred) andre@0: { andre@0: /* andre@0: * PR_FALSE, because it is unsafe to call back to the andre@0: * thread private data destructors at final cleanup. andre@0: */ andre@0: _pt_thread_death_internal(thred, PR_FALSE); andre@0: rv = pthread_setspecific(pt_book.key, NULL); andre@0: PR_ASSERT(0 == rv); andre@0: } andre@0: rv = pthread_key_delete(pt_book.key); andre@0: PR_ASSERT(0 == rv); andre@0: pt_book.keyCreated = PR_FALSE; andre@0: /* TODO: free other resources used by NSPR */ andre@0: /* _pr_initialized = PR_FALSE; */ andre@0: } /* _PR_Fini */ andre@0: andre@0: PR_IMPLEMENT(PRStatus) PR_Cleanup(void) andre@0: { andre@0: PRThread *me = PR_GetCurrentThread(); andre@0: int rv; andre@0: PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR")); andre@0: PR_ASSERT(me->state & PT_THREAD_PRIMORD); andre@0: if (me->state & PT_THREAD_PRIMORD) andre@0: { andre@0: PR_Lock(pt_book.ml); andre@0: while (pt_book.user > pt_book.this_many) andre@0: PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); andre@0: if (me->state & PT_THREAD_SYSTEM) andre@0: pt_book.system -= 1; andre@0: else andre@0: pt_book.user -= 1; andre@0: PR_Unlock(pt_book.ml); andre@0: andre@0: _PR_MD_EARLY_CLEANUP(); andre@0: andre@0: _PR_CleanupMW(); andre@0: _PR_CleanupTime(); andre@0: _PR_CleanupDtoa(); andre@0: _PR_CleanupCallOnce(); andre@0: _PR_ShutdownLinker(); andre@0: _PR_LogCleanup(); andre@0: _PR_CleanupNet(); andre@0: /* Close all the fd's before calling _PR_CleanupIO */ andre@0: _PR_CleanupIO(); andre@0: _PR_CleanupCMon(); andre@0: andre@0: _pt_thread_death(me); andre@0: rv = pthread_setspecific(pt_book.key, NULL); andre@0: PR_ASSERT(0 == rv); andre@0: /* andre@0: * I am not sure if it's safe to delete the cv and lock here, andre@0: * since there may still be "system" threads around. If this andre@0: * call isn't immediately prior to exiting, then there's a andre@0: * problem. andre@0: */ andre@0: if (0 == pt_book.system) andre@0: { andre@0: PR_DestroyCondVar(pt_book.cv); pt_book.cv = NULL; andre@0: PR_DestroyLock(pt_book.ml); pt_book.ml = NULL; andre@0: } andre@0: PR_DestroyLock(_pr_sleeplock); andre@0: _pr_sleeplock = NULL; andre@0: _PR_CleanupLayerCache(); andre@0: _PR_CleanupEnv(); andre@0: #ifdef _PR_ZONE_ALLOCATOR andre@0: _PR_DestroyZones(); andre@0: #endif andre@0: _pr_initialized = PR_FALSE; andre@0: return PR_SUCCESS; andre@0: } andre@0: return PR_FAILURE; andre@0: } /* PR_Cleanup */ andre@0: andre@0: PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status) andre@0: { andre@0: _exit(status); andre@0: } andre@0: andre@0: PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thred) andre@0: { andre@0: #if defined(_PR_DCETHREADS) andre@0: return (PRUint32)&thred->id; /* this is really a sham! */ andre@0: #else andre@0: return (PRUint32)thred->id; /* and I don't know what they will do with it */ andre@0: #endif andre@0: } andre@0: andre@0: /* andre@0: * $$$ andre@0: * The following two thread-to-processor affinity functions are not andre@0: * yet implemented for pthreads. By the way, these functions should return andre@0: * PRStatus rather than PRInt32 to indicate the success/failure status. andre@0: * $$$ andre@0: */ andre@0: andre@0: PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask) andre@0: { andre@0: return 0; /* not implemented */ andre@0: } andre@0: andre@0: PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask ) andre@0: { andre@0: return 0; /* not implemented */ andre@0: } andre@0: andre@0: PR_IMPLEMENT(void) andre@0: PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg) andre@0: { andre@0: thread->dump = dump; andre@0: thread->dumpArg = arg; andre@0: } andre@0: andre@0: /* andre@0: * Garbage collection support follows. andre@0: */ andre@0: andre@0: #if defined(_PR_DCETHREADS) andre@0: andre@0: /* andre@0: * statics for Garbage Collection support. We don't need to protect these andre@0: * signal masks since the garbage collector itself is protected by a lock andre@0: * and multiple threads will not be garbage collecting at the same time. andre@0: */ andre@0: static sigset_t javagc_vtalarm_sigmask; andre@0: static sigset_t javagc_intsoff_sigmask; andre@0: andre@0: #else /* defined(_PR_DCETHREADS) */ andre@0: andre@0: /* a bogus signal mask for forcing a timed wait */ andre@0: /* Not so bogus in AIX as we really do a sigwait */ andre@0: static sigset_t sigwait_set; andre@0: andre@0: static struct timespec onemillisec = {0, 1000000L}; andre@0: #ifndef PT_NO_SIGTIMEDWAIT andre@0: static struct timespec hundredmillisec = {0, 100000000L}; andre@0: #endif andre@0: andre@0: static void suspend_signal_handler(PRIntn sig); andre@0: andre@0: #ifdef PT_NO_SIGTIMEDWAIT andre@0: static void null_signal_handler(PRIntn sig); andre@0: #endif andre@0: andre@0: #endif /* defined(_PR_DCETHREADS) */ andre@0: andre@0: /* andre@0: * Linux pthreads use SIGUSR1 and SIGUSR2 internally, which andre@0: * conflict with the use of these two signals in our GC support. andre@0: * So we don't know how to support GC on Linux pthreads. andre@0: */ andre@0: static void init_pthread_gc_support(void) andre@0: { andre@0: #ifndef SYMBIAN andre@0: PRIntn rv; andre@0: andre@0: #if defined(_PR_DCETHREADS) andre@0: rv = sigemptyset(&javagc_vtalarm_sigmask); andre@0: PR_ASSERT(0 == rv); andre@0: rv = sigaddset(&javagc_vtalarm_sigmask, SIGVTALRM); andre@0: PR_ASSERT(0 == rv); andre@0: #else /* defined(_PR_DCETHREADS) */ andre@0: { andre@0: struct sigaction sigact_usr2; andre@0: andre@0: sigact_usr2.sa_handler = suspend_signal_handler; andre@0: sigact_usr2.sa_flags = SA_RESTART; andre@0: sigemptyset (&sigact_usr2.sa_mask); andre@0: andre@0: rv = sigaction (SIGUSR2, &sigact_usr2, NULL); andre@0: PR_ASSERT(0 == rv); andre@0: andre@0: sigemptyset (&sigwait_set); andre@0: #if defined(PT_NO_SIGTIMEDWAIT) andre@0: sigaddset (&sigwait_set, SIGUSR1); andre@0: #else andre@0: sigaddset (&sigwait_set, SIGUSR2); andre@0: #endif /* defined(PT_NO_SIGTIMEDWAIT) */ andre@0: } andre@0: #if defined(PT_NO_SIGTIMEDWAIT) andre@0: { andre@0: struct sigaction sigact_null; andre@0: sigact_null.sa_handler = null_signal_handler; andre@0: sigact_null.sa_flags = SA_RESTART; andre@0: sigemptyset (&sigact_null.sa_mask); andre@0: rv = sigaction (SIGUSR1, &sigact_null, NULL); andre@0: PR_ASSERT(0 ==rv); andre@0: } andre@0: #endif /* defined(PT_NO_SIGTIMEDWAIT) */ andre@0: #endif /* defined(_PR_DCETHREADS) */ andre@0: #endif /* SYMBIAN */ andre@0: } andre@0: andre@0: PR_IMPLEMENT(void) PR_SetThreadGCAble(void) andre@0: { andre@0: PR_Lock(pt_book.ml); andre@0: PR_GetCurrentThread()->state |= PT_THREAD_GCABLE; andre@0: PR_Unlock(pt_book.ml); andre@0: } andre@0: andre@0: PR_IMPLEMENT(void) PR_ClearThreadGCAble(void) andre@0: { andre@0: PR_Lock(pt_book.ml); andre@0: PR_GetCurrentThread()->state &= (~PT_THREAD_GCABLE); andre@0: PR_Unlock(pt_book.ml); andre@0: } andre@0: andre@0: #if defined(DEBUG) andre@0: static PRBool suspendAllOn = PR_FALSE; andre@0: #endif andre@0: andre@0: static PRBool suspendAllSuspended = PR_FALSE; andre@0: andre@0: PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg) andre@0: { andre@0: PRIntn count = 0; andre@0: PRStatus rv = PR_SUCCESS; andre@0: PRThread* thred = pt_book.first; andre@0: andre@0: #if defined(DEBUG) || defined(FORCE_PR_ASSERT) andre@0: #if !defined(_PR_DCETHREADS) andre@0: PRThread *me = PR_GetCurrentThread(); andre@0: #endif andre@0: #endif andre@0: andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_EnumerateThreads\n")); andre@0: /* andre@0: * $$$ andre@0: * Need to suspend all threads other than me before doing this. andre@0: * This is really a gross and disgusting thing to do. The only andre@0: * good thing is that since all other threads are suspended, holding andre@0: * the lock during a callback seems like child's play. andre@0: * $$$ andre@0: */ andre@0: PR_ASSERT(suspendAllOn); andre@0: andre@0: while (thred != NULL) andre@0: { andre@0: /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking andre@0: * qp->next after applying the function "func". In particular, "func" andre@0: * might remove the thread from the queue and put it into another one in andre@0: * which case qp->next no longer points to the next entry in the original andre@0: * queue. andre@0: * andre@0: * To get around this problem, we save qp->next in qp_next before applying andre@0: * "func" and use that saved value as the next value after applying "func". andre@0: */ andre@0: PRThread* next = thred->next; andre@0: andre@0: if (_PT_IS_GCABLE_THREAD(thred)) andre@0: { andre@0: #if !defined(_PR_DCETHREADS) andre@0: PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED)); andre@0: #endif andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, andre@0: ("In PR_EnumerateThreads callback thread %p thid = %X\n", andre@0: thred, thred->id)); andre@0: andre@0: rv = func(thred, count++, arg); andre@0: if (rv != PR_SUCCESS) andre@0: return rv; andre@0: } andre@0: thred = next; andre@0: } andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, andre@0: ("End PR_EnumerateThreads count = %d \n", count)); andre@0: return rv; andre@0: } /* PR_EnumerateThreads */ andre@0: andre@0: /* andre@0: * PR_SuspendAll and PR_ResumeAll are called during garbage collection. The strategy andre@0: * we use is to send a SIGUSR2 signal to every gc able thread that we intend to suspend. andre@0: * The signal handler will record the stack pointer and will block until resumed by andre@0: * the resume call. Since the signal handler is the last routine called for the andre@0: * suspended thread, the stack pointer will also serve as a place where all the andre@0: * registers have been saved on the stack for the previously executing routines. andre@0: * andre@0: * Through global variables, we also make sure that PR_Suspend and PR_Resume does not andre@0: * proceed until the thread is suspended or resumed. andre@0: */ andre@0: andre@0: #if !defined(_PR_DCETHREADS) andre@0: andre@0: /* andre@0: * In the signal handler, we can not use condition variable notify or wait. andre@0: * This does not work consistently across all pthread platforms. We also can not andre@0: * use locking since that does not seem to work reliably across platforms. andre@0: * Only thing we can do is yielding while testing for a global condition andre@0: * to change. This does work on pthread supported platforms. We may have andre@0: * to play with priortities if there are any problems detected. andre@0: */ andre@0: andre@0: /* andre@0: * In AIX, you cannot use ANY pthread calls in the signal handler except perhaps andre@0: * pthread_yield. But that is horribly inefficient. Hence we use only sigwait, no andre@0: * sigtimedwait is available. We need to use another user signal, SIGUSR1. Actually andre@0: * SIGUSR1 is also used by exec in Java. So our usage here breaks the exec in Java, andre@0: * for AIX. You cannot use pthread_cond_wait or pthread_delay_np in the signal andre@0: * handler as all synchronization mechanisms just break down. andre@0: */ andre@0: andre@0: #if defined(PT_NO_SIGTIMEDWAIT) andre@0: static void null_signal_handler(PRIntn sig) andre@0: { andre@0: return; andre@0: } andre@0: #endif andre@0: andre@0: static void suspend_signal_handler(PRIntn sig) andre@0: { andre@0: PRThread *me = PR_GetCurrentThread(); andre@0: andre@0: PR_ASSERT(me != NULL); andre@0: PR_ASSERT(_PT_IS_GCABLE_THREAD(me)); andre@0: PR_ASSERT((me->suspend & PT_THREAD_SUSPENDED) == 0); andre@0: andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, andre@0: ("Begin suspend_signal_handler thred %p thread id = %X\n", andre@0: me, me->id)); andre@0: andre@0: /* andre@0: * save stack pointer andre@0: */ andre@0: me->sp = &me; andre@0: andre@0: /* andre@0: At this point, the thread's stack pointer has been saved, andre@0: And it is going to enter a wait loop until it is resumed. andre@0: So it is _really_ suspended andre@0: */ andre@0: andre@0: me->suspend |= PT_THREAD_SUSPENDED; andre@0: andre@0: /* andre@0: * now, block current thread andre@0: */ andre@0: #if defined(PT_NO_SIGTIMEDWAIT) andre@0: pthread_cond_signal(&me->suspendResumeCV); andre@0: while (me->suspend & PT_THREAD_SUSPENDED) andre@0: { andre@0: #if !defined(FREEBSD) && !defined(NETBSD) && !defined(OPENBSD) \ andre@0: && !defined(BSDI) && !defined(UNIXWARE) \ andre@0: && !defined(DARWIN) && !defined(RISCOS) \ andre@0: && !defined(SYMBIAN) /*XXX*/ andre@0: PRIntn rv; andre@0: sigwait(&sigwait_set, &rv); andre@0: #endif andre@0: } andre@0: me->suspend |= PT_THREAD_RESUMED; andre@0: pthread_cond_signal(&me->suspendResumeCV); andre@0: #else /* defined(PT_NO_SIGTIMEDWAIT) */ andre@0: while (me->suspend & PT_THREAD_SUSPENDED) andre@0: { andre@0: PRIntn rv = sigtimedwait(&sigwait_set, NULL, &hundredmillisec); andre@0: PR_ASSERT(-1 == rv); andre@0: } andre@0: me->suspend |= PT_THREAD_RESUMED; andre@0: #endif andre@0: andre@0: /* andre@0: * At this point, thread has been resumed, so set a global condition. andre@0: * The ResumeAll needs to know that this has really been resumed. andre@0: * So the signal handler sets a flag which PR_ResumeAll will reset. andre@0: * The PR_ResumeAll must reset this flag ... andre@0: */ andre@0: andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, andre@0: ("End suspend_signal_handler thred = %p tid = %X\n", me, me->id)); andre@0: } /* suspend_signal_handler */ andre@0: andre@0: static void pt_SuspendSet(PRThread *thred) andre@0: { andre@0: PRIntn rv; andre@0: andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, andre@0: ("pt_SuspendSet thred %p thread id = %X\n", thred, thred->id)); andre@0: andre@0: andre@0: /* andre@0: * Check the thread state and signal the thread to suspend andre@0: */ andre@0: andre@0: PR_ASSERT((thred->suspend & PT_THREAD_SUSPENDED) == 0); andre@0: andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, andre@0: ("doing pthread_kill in pt_SuspendSet thred %p tid = %X\n", andre@0: thred, thred->id)); andre@0: #if defined(SYMBIAN) andre@0: /* All signal group functions are not implemented in Symbian OS */ andre@0: rv = 0; andre@0: #else andre@0: rv = pthread_kill (thred->id, SIGUSR2); andre@0: #endif andre@0: PR_ASSERT(0 == rv); andre@0: } andre@0: andre@0: static void pt_SuspendTest(PRThread *thred) andre@0: { andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, andre@0: ("Begin pt_SuspendTest thred %p thread id = %X\n", thred, thred->id)); andre@0: andre@0: andre@0: /* andre@0: * Wait for the thread to be really suspended. This happens when the andre@0: * suspend signal handler stores the stack pointer and sets the state andre@0: * to suspended. andre@0: */ andre@0: andre@0: #if defined(PT_NO_SIGTIMEDWAIT) andre@0: pthread_mutex_lock(&thred->suspendResumeMutex); andre@0: while ((thred->suspend & PT_THREAD_SUSPENDED) == 0) andre@0: { andre@0: pthread_cond_timedwait( andre@0: &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec); andre@0: } andre@0: pthread_mutex_unlock(&thred->suspendResumeMutex); andre@0: #else andre@0: while ((thred->suspend & PT_THREAD_SUSPENDED) == 0) andre@0: { andre@0: PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec); andre@0: PR_ASSERT(-1 == rv); andre@0: } andre@0: #endif andre@0: andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, andre@0: ("End pt_SuspendTest thred %p tid %X\n", thred, thred->id)); andre@0: } /* pt_SuspendTest */ andre@0: andre@0: static void pt_ResumeSet(PRThread *thred) andre@0: { andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, andre@0: ("pt_ResumeSet thred %p thread id = %X\n", thred, thred->id)); andre@0: andre@0: /* andre@0: * Clear the global state and set the thread state so that it will andre@0: * continue past yield loop in the suspend signal handler andre@0: */ andre@0: andre@0: PR_ASSERT(thred->suspend & PT_THREAD_SUSPENDED); andre@0: andre@0: andre@0: thred->suspend &= ~PT_THREAD_SUSPENDED; andre@0: andre@0: #if defined(PT_NO_SIGTIMEDWAIT) andre@0: #if defined(SYMBIAN) andre@0: /* All signal group functions are not implemented in Symbian OS */ andre@0: #else andre@0: pthread_kill(thred->id, SIGUSR1); andre@0: #endif andre@0: #endif andre@0: andre@0: } /* pt_ResumeSet */ andre@0: andre@0: static void pt_ResumeTest(PRThread *thred) andre@0: { andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, andre@0: ("Begin pt_ResumeTest thred %p thread id = %X\n", thred, thred->id)); andre@0: andre@0: /* andre@0: * Wait for the threads resume state to change andre@0: * to indicate it is really resumed andre@0: */ andre@0: #if defined(PT_NO_SIGTIMEDWAIT) andre@0: pthread_mutex_lock(&thred->suspendResumeMutex); andre@0: while ((thred->suspend & PT_THREAD_RESUMED) == 0) andre@0: { andre@0: pthread_cond_timedwait( andre@0: &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec); andre@0: } andre@0: pthread_mutex_unlock(&thred->suspendResumeMutex); andre@0: #else andre@0: while ((thred->suspend & PT_THREAD_RESUMED) == 0) { andre@0: PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec); andre@0: PR_ASSERT(-1 == rv); andre@0: } andre@0: #endif andre@0: andre@0: thred->suspend &= ~PT_THREAD_RESUMED; andre@0: andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ( andre@0: "End pt_ResumeTest thred %p tid %X\n", thred, thred->id)); andre@0: } /* pt_ResumeTest */ andre@0: andre@0: static pthread_once_t pt_gc_support_control = PTHREAD_ONCE_INIT; andre@0: andre@0: PR_IMPLEMENT(void) PR_SuspendAll(void) andre@0: { andre@0: #ifdef DEBUG andre@0: PRIntervalTime stime, etime; andre@0: #endif andre@0: PRThread* thred = pt_book.first; andre@0: PRThread *me = PR_GetCurrentThread(); andre@0: int rv; andre@0: andre@0: rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support); andre@0: PR_ASSERT(0 == rv); andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n")); andre@0: /* andre@0: * Stop all threads which are marked GC able. andre@0: */ andre@0: PR_Lock(pt_book.ml); andre@0: #ifdef DEBUG andre@0: suspendAllOn = PR_TRUE; andre@0: stime = PR_IntervalNow(); andre@0: #endif andre@0: while (thred != NULL) andre@0: { andre@0: if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) andre@0: pt_SuspendSet(thred); andre@0: thred = thred->next; andre@0: } andre@0: andre@0: /* Wait till they are really suspended */ andre@0: thred = pt_book.first; andre@0: while (thred != NULL) andre@0: { andre@0: if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) andre@0: pt_SuspendTest(thred); andre@0: thred = thred->next; andre@0: } andre@0: andre@0: suspendAllSuspended = PR_TRUE; andre@0: andre@0: #ifdef DEBUG andre@0: etime = PR_IntervalNow(); andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,\ andre@0: ("End PR_SuspendAll (time %dms)\n", andre@0: PR_IntervalToMilliseconds(etime - stime))); andre@0: #endif andre@0: } /* PR_SuspendAll */ andre@0: andre@0: PR_IMPLEMENT(void) PR_ResumeAll(void) andre@0: { andre@0: #ifdef DEBUG andre@0: PRIntervalTime stime, etime; andre@0: #endif andre@0: PRThread* thred = pt_book.first; andre@0: PRThread *me = PR_GetCurrentThread(); andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n")); andre@0: /* andre@0: * Resume all previously suspended GC able threads. andre@0: */ andre@0: suspendAllSuspended = PR_FALSE; andre@0: #ifdef DEBUG andre@0: stime = PR_IntervalNow(); andre@0: #endif andre@0: andre@0: while (thred != NULL) andre@0: { andre@0: if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) andre@0: pt_ResumeSet(thred); andre@0: thred = thred->next; andre@0: } andre@0: andre@0: thred = pt_book.first; andre@0: while (thred != NULL) andre@0: { andre@0: if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) andre@0: pt_ResumeTest(thred); andre@0: thred = thred->next; andre@0: } andre@0: andre@0: PR_Unlock(pt_book.ml); andre@0: #ifdef DEBUG andre@0: suspendAllOn = PR_FALSE; andre@0: etime = PR_IntervalNow(); andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, andre@0: ("End PR_ResumeAll (time %dms)\n", andre@0: PR_IntervalToMilliseconds(etime - stime))); andre@0: #endif andre@0: } /* PR_ResumeAll */ andre@0: andre@0: /* Return the stack pointer for the given thread- used by the GC */ andre@0: PR_IMPLEMENT(void *)PR_GetSP(PRThread *thred) andre@0: { andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, andre@0: ("in PR_GetSP thred %p thid = %X, sp = %p\n", andre@0: thred, thred->id, thred->sp)); andre@0: return thred->sp; andre@0: } /* PR_GetSP */ andre@0: andre@0: #else /* !defined(_PR_DCETHREADS) */ andre@0: andre@0: static pthread_once_t pt_gc_support_control = pthread_once_init; andre@0: andre@0: /* andre@0: * For DCE threads, there is no pthread_kill or a way of suspending or resuming a andre@0: * particular thread. We will just disable the preemption (virtual timer alarm) and andre@0: * let the executing thread finish the garbage collection. This stops all other threads andre@0: * (GC able or not) and is very inefficient but there is no other choice. andre@0: */ andre@0: PR_IMPLEMENT(void) PR_SuspendAll() andre@0: { andre@0: PRIntn rv; andre@0: andre@0: rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support); andre@0: PR_ASSERT(0 == rv); /* returns -1 on failure */ andre@0: #ifdef DEBUG andre@0: suspendAllOn = PR_TRUE; andre@0: #endif andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n")); andre@0: /* andre@0: * turn off preemption - i.e add virtual alarm signal to the set of andre@0: * blocking signals andre@0: */ andre@0: rv = sigprocmask( andre@0: SIG_BLOCK, &javagc_vtalarm_sigmask, &javagc_intsoff_sigmask); andre@0: PR_ASSERT(0 == rv); andre@0: suspendAllSuspended = PR_TRUE; andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_SuspendAll\n")); andre@0: } /* PR_SuspendAll */ andre@0: andre@0: PR_IMPLEMENT(void) PR_ResumeAll() andre@0: { andre@0: PRIntn rv; andre@0: andre@0: suspendAllSuspended = PR_FALSE; andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n")); andre@0: /* turn on preemption - i.e re-enable virtual alarm signal */ andre@0: andre@0: rv = sigprocmask(SIG_SETMASK, &javagc_intsoff_sigmask, (sigset_t *)NULL); andre@0: PR_ASSERT(0 == rv); andre@0: #ifdef DEBUG andre@0: suspendAllOn = PR_FALSE; andre@0: #endif andre@0: andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_ResumeAll\n")); andre@0: } /* PR_ResumeAll */ andre@0: andre@0: /* Return the stack pointer for the given thread- used by the GC */ andre@0: PR_IMPLEMENT(void*)PR_GetSP(PRThread *thred) andre@0: { andre@0: pthread_t tid = thred->id; andre@0: char *thread_tcb, *top_sp; andre@0: andre@0: /* andre@0: * For HPUX DCE threads, pthread_t is a struct with the andre@0: * following three fields (see pthread.h, dce/cma.h): andre@0: * cma_t_address field1; andre@0: * short int field2; andre@0: * short int field3; andre@0: * where cma_t_address is typedef'd to be either void* andre@0: * or char*. andre@0: */ andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_GetSP\n")); andre@0: thread_tcb = (char*)tid.field1; andre@0: top_sp = *(char**)(thread_tcb + 128); andre@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_GetSP %p \n", top_sp)); andre@0: return top_sp; andre@0: } /* PR_GetSP */ andre@0: andre@0: #endif /* !defined(_PR_DCETHREADS) */ andre@0: andre@0: PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char *name) andre@0: { andre@0: PRThread *thread; andre@0: size_t nameLen; andre@0: int result; andre@0: andre@0: if (!name) { andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: thread = PR_GetCurrentThread(); andre@0: if (!thread) andre@0: return PR_FAILURE; andre@0: andre@0: PR_Free(thread->name); andre@0: nameLen = strlen(name); andre@0: thread->name = (char *)PR_Malloc(nameLen + 1); andre@0: if (!thread->name) andre@0: return PR_FAILURE; andre@0: memcpy(thread->name, name, nameLen + 1); andre@0: andre@0: #if defined(OPENBSD) || defined(FREEBSD) andre@0: result = pthread_set_name_np(thread->id, name); andre@0: #else /* not BSD */ andre@0: /* andre@0: * On OSX, pthread_setname_np is only available in 10.6 or later, so test andre@0: * for it at runtime. It also may not be available on all linux distros. andre@0: */ andre@0: #if defined(DARWIN) andre@0: int (*dynamic_pthread_setname_np)(const char*); andre@0: #else andre@0: int (*dynamic_pthread_setname_np)(pthread_t, const char*); andre@0: #endif andre@0: andre@0: *(void**)(&dynamic_pthread_setname_np) = andre@0: dlsym(RTLD_DEFAULT, "pthread_setname_np"); andre@0: if (!dynamic_pthread_setname_np) andre@0: return PR_SUCCESS; andre@0: andre@0: /* andre@0: * The 15-character name length limit is an experimentally determined andre@0: * length of a null-terminated string that most linux distros and OS X andre@0: * accept as an argument to pthread_setname_np. Otherwise the E2BIG andre@0: * error is returned by the function. andre@0: */ andre@0: #define SETNAME_LENGTH_CONSTRAINT 15 andre@0: #define SETNAME_FRAGMENT1_LENGTH (SETNAME_LENGTH_CONSTRAINT >> 1) andre@0: #define SETNAME_FRAGMENT2_LENGTH \ andre@0: (SETNAME_LENGTH_CONSTRAINT - SETNAME_FRAGMENT1_LENGTH - 1) andre@0: char name_dup[SETNAME_LENGTH_CONSTRAINT + 1]; andre@0: if (nameLen > SETNAME_LENGTH_CONSTRAINT) { andre@0: memcpy(name_dup, name, SETNAME_FRAGMENT1_LENGTH); andre@0: name_dup[SETNAME_FRAGMENT1_LENGTH] = '~'; andre@0: /* Note that this also copies the null terminator. */ andre@0: memcpy(name_dup + SETNAME_FRAGMENT1_LENGTH + 1, andre@0: name + nameLen - SETNAME_FRAGMENT2_LENGTH, andre@0: SETNAME_FRAGMENT2_LENGTH + 1); andre@0: name = name_dup; andre@0: } andre@0: andre@0: #if defined(DARWIN) andre@0: result = dynamic_pthread_setname_np(name); andre@0: #else andre@0: result = dynamic_pthread_setname_np(thread->id, name); andre@0: #endif andre@0: #endif /* not BSD */ andre@0: andre@0: if (result) { andre@0: PR_SetError(PR_UNKNOWN_ERROR, result); andre@0: return PR_FAILURE; andre@0: } andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread) andre@0: { andre@0: if (!thread) andre@0: return NULL; andre@0: return thread->name; andre@0: } andre@0: andre@0: #endif /* defined(_PR_PTHREADS) || defined(_PR_DCETHREADS) */ andre@0: andre@0: /* ptthread.c */