michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "primpl.h" michael@0: michael@0: #if defined(WIN95) michael@0: /* michael@0: ** Some local variables report warnings on Win95 because the code paths michael@0: ** using them are conditioned on HAVE_CUSTOME_USER_THREADS. michael@0: ** The pragma suppresses the warning. michael@0: ** michael@0: */ michael@0: #pragma warning(disable : 4101) michael@0: #endif michael@0: michael@0: michael@0: extern PRLock *_pr_sleeplock; /* allocated and initialized in prinit */ michael@0: /* michael@0: ** Routines common to both native and user threads. michael@0: ** michael@0: ** michael@0: ** Clean up a thread object, releasing all of the attached data. Do not michael@0: ** free the object itself (it may not have been malloc'd) michael@0: */ michael@0: void _PR_CleanupThread(PRThread *thread) michael@0: { michael@0: /* Free up per-thread-data */ michael@0: _PR_DestroyThreadPrivate(thread); michael@0: michael@0: /* Free any thread dump procs */ michael@0: if (thread->dumpArg) { michael@0: PR_DELETE(thread->dumpArg); michael@0: } michael@0: thread->dump = 0; michael@0: michael@0: PR_DELETE(thread->name); michael@0: PR_DELETE(thread->errorString); michael@0: thread->errorStringSize = 0; michael@0: thread->errorStringLength = 0; michael@0: thread->environment = NULL; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_Yield() michael@0: { michael@0: static PRBool warning = PR_TRUE; michael@0: if (warning) warning = _PR_Obsolete( michael@0: "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)"); michael@0: return (PR_Sleep(PR_INTERVAL_NO_WAIT)); michael@0: } michael@0: michael@0: /* michael@0: ** Make the current thread sleep until "timeout" ticks amount of time michael@0: ** has expired. If "timeout" is PR_INTERVAL_NO_WAIT then the call is michael@0: ** equivalent to a yield. Waiting for an infinite amount of time is michael@0: ** allowed in the expectation that another thread will interrupt(). michael@0: ** michael@0: ** A single lock is used for all threads calling sleep. Each caller michael@0: ** does get its own condition variable since each is expected to have michael@0: ** a unique 'timeout'. michael@0: */ michael@0: PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime timeout) michael@0: { michael@0: PRStatus rv = PR_SUCCESS; michael@0: michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: michael@0: if (PR_INTERVAL_NO_WAIT == timeout) michael@0: { michael@0: /* michael@0: ** This is a simple yield, nothing more, nothing less. michael@0: */ michael@0: PRIntn is; michael@0: PRThread *me = PR_GetCurrentThread(); michael@0: PRUintn pri = me->priority; michael@0: _PRCPU *cpu = _PR_MD_CURRENT_CPU(); michael@0: michael@0: if ( _PR_IS_NATIVE_THREAD(me) ) _PR_MD_YIELD(); michael@0: else michael@0: { michael@0: _PR_INTSOFF(is); michael@0: _PR_RUNQ_LOCK(cpu); michael@0: if (_PR_RUNQREADYMASK(cpu) >> pri) { michael@0: me->cpu = cpu; michael@0: me->state = _PR_RUNNABLE; michael@0: _PR_ADD_RUNQ(me, cpu, pri); michael@0: _PR_RUNQ_UNLOCK(cpu); michael@0: michael@0: PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("PR_Yield: yielding")); michael@0: _PR_MD_SWITCH_CONTEXT(me); michael@0: PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("PR_Yield: done")); michael@0: michael@0: _PR_FAST_INTSON(is); michael@0: } michael@0: else michael@0: { michael@0: _PR_RUNQ_UNLOCK(cpu); michael@0: _PR_INTSON(is); michael@0: } michael@0: } michael@0: } michael@0: else michael@0: { michael@0: /* michael@0: ** This is waiting for some finite period of time. michael@0: ** A thread in this state is interruptible (PR_Interrupt()), michael@0: ** but the lock and cvar used are local to the implementation michael@0: ** and not visible to the caller, therefore not notifiable. michael@0: */ michael@0: PRCondVar *cv; michael@0: PRIntervalTime timein; michael@0: michael@0: timein = PR_IntervalNow(); michael@0: cv = PR_NewCondVar(_pr_sleeplock); michael@0: PR_ASSERT(cv != NULL); michael@0: PR_Lock(_pr_sleeplock); michael@0: do michael@0: { michael@0: PRIntervalTime delta = PR_IntervalNow() - timein; michael@0: if (delta > timeout) break; michael@0: rv = PR_WaitCondVar(cv, timeout - delta); michael@0: } while (rv == PR_SUCCESS); michael@0: PR_Unlock(_pr_sleeplock); michael@0: PR_DestroyCondVar(cv); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thread) michael@0: { michael@0: return thread->id; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thread) michael@0: { michael@0: return (PRThreadPriority) thread->priority; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThread *) PR_GetCurrentThread() michael@0: { michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: return _PR_MD_CURRENT_THREAD(); michael@0: } michael@0: michael@0: /* michael@0: ** Set the interrupt flag for a thread. The thread will be unable to michael@0: ** block in i/o functions when this happens. Also, any PR_Wait's in michael@0: ** progress will be undone. The interrupt remains in force until michael@0: ** PR_ClearInterrupt is called. michael@0: */ michael@0: PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thread) michael@0: { michael@0: #ifdef _PR_GLOBAL_THREADS_ONLY michael@0: PRCondVar *victim; michael@0: michael@0: _PR_THREAD_LOCK(thread); michael@0: thread->flags |= _PR_INTERRUPT; michael@0: victim = thread->wait.cvar; michael@0: _PR_THREAD_UNLOCK(thread); michael@0: if ((NULL != victim) && (!(thread->flags & _PR_INTERRUPT_BLOCKED))) { michael@0: int haveLock = (victim->lock->owner == _PR_MD_CURRENT_THREAD()); michael@0: michael@0: if (!haveLock) PR_Lock(victim->lock); michael@0: PR_NotifyAllCondVar(victim); michael@0: if (!haveLock) PR_Unlock(victim->lock); michael@0: } michael@0: return PR_SUCCESS; michael@0: #else /* ! _PR_GLOBAL_THREADS_ONLY */ michael@0: PRIntn is; michael@0: PRThread *me = _PR_MD_CURRENT_THREAD(); michael@0: michael@0: if (!_PR_IS_NATIVE_THREAD(me)) michael@0: _PR_INTSOFF(is); michael@0: michael@0: _PR_THREAD_LOCK(thread); michael@0: thread->flags |= _PR_INTERRUPT; michael@0: switch (thread->state) { michael@0: case _PR_COND_WAIT: michael@0: /* michael@0: * call is made with thread locked; michael@0: * on return lock is released michael@0: */ michael@0: if (!(thread->flags & _PR_INTERRUPT_BLOCKED)) michael@0: _PR_NotifyLockedThread(thread); michael@0: break; michael@0: case _PR_IO_WAIT: michael@0: /* michael@0: * Need to hold the thread lock when calling michael@0: * _PR_Unblock_IO_Wait(). On return lock is michael@0: * released. michael@0: */ michael@0: #if defined(XP_UNIX) || defined(WINNT) || defined(WIN16) michael@0: if (!(thread->flags & _PR_INTERRUPT_BLOCKED)) michael@0: _PR_Unblock_IO_Wait(thread); michael@0: #else michael@0: _PR_THREAD_UNLOCK(thread); michael@0: #endif michael@0: break; michael@0: case _PR_RUNNING: michael@0: case _PR_RUNNABLE: michael@0: case _PR_LOCK_WAIT: michael@0: default: michael@0: _PR_THREAD_UNLOCK(thread); michael@0: break; michael@0: } michael@0: if (!_PR_IS_NATIVE_THREAD(me)) michael@0: _PR_INTSON(is); michael@0: return PR_SUCCESS; michael@0: #endif /* _PR_GLOBAL_THREADS_ONLY */ michael@0: } michael@0: michael@0: /* michael@0: ** Clear the interrupt flag for self. michael@0: */ michael@0: PR_IMPLEMENT(void) PR_ClearInterrupt() michael@0: { michael@0: PRIntn is; michael@0: PRThread *me = _PR_MD_CURRENT_THREAD(); michael@0: michael@0: if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); michael@0: _PR_THREAD_LOCK(me); michael@0: me->flags &= ~_PR_INTERRUPT; michael@0: _PR_THREAD_UNLOCK(me); michael@0: if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); michael@0: } michael@0: michael@0: PR_IMPLEMENT(void) PR_BlockInterrupt() michael@0: { michael@0: PRIntn is; michael@0: PRThread *me = _PR_MD_CURRENT_THREAD(); michael@0: michael@0: if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); michael@0: _PR_THREAD_LOCK(me); michael@0: _PR_THREAD_BLOCK_INTERRUPT(me); michael@0: _PR_THREAD_UNLOCK(me); michael@0: if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); michael@0: } /* PR_BlockInterrupt */ michael@0: michael@0: PR_IMPLEMENT(void) PR_UnblockInterrupt() michael@0: { michael@0: PRIntn is; michael@0: PRThread *me = _PR_MD_CURRENT_THREAD(); michael@0: michael@0: if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); michael@0: _PR_THREAD_LOCK(me); michael@0: _PR_THREAD_UNBLOCK_INTERRUPT(me); michael@0: _PR_THREAD_UNLOCK(me); michael@0: if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); michael@0: } /* PR_UnblockInterrupt */ michael@0: michael@0: /* michael@0: ** Return the thread stack pointer of the given thread. michael@0: */ michael@0: PR_IMPLEMENT(void *) PR_GetSP(PRThread *thread) michael@0: { michael@0: return (void *)_PR_MD_GET_SP(thread); michael@0: } michael@0: michael@0: PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thread) michael@0: { michael@0: return thread->environment; michael@0: } michael@0: michael@0: PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thread, void *env) michael@0: { michael@0: thread->environment = env; michael@0: } michael@0: michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask) michael@0: { michael@0: #ifdef HAVE_THREAD_AFFINITY michael@0: return _PR_MD_GETTHREADAFFINITYMASK(thread, mask); michael@0: #else michael@0: return 0; michael@0: #endif michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask ) michael@0: { michael@0: #ifdef HAVE_THREAD_AFFINITY michael@0: #ifndef IRIX michael@0: return _PR_MD_SETTHREADAFFINITYMASK(thread, mask); michael@0: #else michael@0: return 0; michael@0: #endif michael@0: #else michael@0: return 0; michael@0: #endif michael@0: } michael@0: michael@0: /* This call is thread unsafe if another thread is calling SetConcurrency() michael@0: */ michael@0: PR_IMPLEMENT(PRInt32) PR_SetCPUAffinityMask(PRUint32 mask) michael@0: { michael@0: #ifdef HAVE_THREAD_AFFINITY michael@0: PRCList *qp; michael@0: extern PRUint32 _pr_cpu_affinity_mask; michael@0: michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: michael@0: _pr_cpu_affinity_mask = mask; michael@0: michael@0: qp = _PR_CPUQ().next; michael@0: while(qp != &_PR_CPUQ()) { michael@0: _PRCPU *cpu; michael@0: michael@0: cpu = _PR_CPU_PTR(qp); michael@0: PR_SetThreadAffinityMask(cpu->thread, mask); michael@0: michael@0: qp = qp->next; michael@0: } michael@0: #endif michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: PRUint32 _pr_recycleThreads = 0; michael@0: PR_IMPLEMENT(void) PR_SetThreadRecycleMode(PRUint32 count) michael@0: { michael@0: _pr_recycleThreads = count; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble(PRThreadType type, michael@0: void (*start)(void *arg), michael@0: void *arg, michael@0: PRThreadPriority priority, michael@0: PRThreadScope scope, michael@0: PRThreadState state, michael@0: PRUint32 stackSize) michael@0: { michael@0: return _PR_CreateThread(type, start, arg, priority, scope, state, michael@0: stackSize, _PR_GCABLE_THREAD); michael@0: } michael@0: michael@0: #ifdef SOLARIS michael@0: PR_IMPLEMENT(PRThread*) PR_CreateThreadBound(PRThreadType type, michael@0: void (*start)(void *arg), michael@0: void *arg, michael@0: PRUintn priority, michael@0: PRThreadScope scope, michael@0: PRThreadState state, michael@0: PRUint32 stackSize) michael@0: { michael@0: return _PR_CreateThread(type, start, arg, priority, scope, state, michael@0: stackSize, _PR_BOUND_THREAD); michael@0: } michael@0: #endif michael@0: michael@0: michael@0: PR_IMPLEMENT(PRThread*) PR_AttachThreadGCAble( michael@0: PRThreadType type, PRThreadPriority priority, PRThreadStack *stack) michael@0: { michael@0: /* $$$$ not sure how to finese this one */ michael@0: PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); michael@0: return NULL; michael@0: } michael@0: michael@0: PR_IMPLEMENT(void) PR_SetThreadGCAble() michael@0: { michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: PR_Lock(_pr_activeLock); michael@0: _PR_MD_CURRENT_THREAD()->flags |= _PR_GCABLE_THREAD; michael@0: PR_Unlock(_pr_activeLock); michael@0: } michael@0: michael@0: PR_IMPLEMENT(void) PR_ClearThreadGCAble() michael@0: { michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: PR_Lock(_pr_activeLock); michael@0: _PR_MD_CURRENT_THREAD()->flags &= (~_PR_GCABLE_THREAD); michael@0: PR_Unlock(_pr_activeLock); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thread) michael@0: { michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: michael@0: if (_PR_IS_NATIVE_THREAD(thread)) { michael@0: return (thread->flags & _PR_BOUND_THREAD) ? PR_GLOBAL_BOUND_THREAD : michael@0: PR_GLOBAL_THREAD; michael@0: } else michael@0: return PR_LOCAL_THREAD; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thread) michael@0: { michael@0: return (thread->flags & _PR_SYSTEM) ? PR_SYSTEM_THREAD : PR_USER_THREAD; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thread) michael@0: { michael@0: return (NULL == thread->term) ? PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD; michael@0: } /* PR_GetThreadState */