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