Bug 1791179 - Fix mistaken conditional in StaticPrefs. r=dragana, a=RyanVM
/* -*- 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"#include<signal.h>#include<string.h>#if defined(WIN95)/*** Some local variables report warnings on Win95 because the code paths** using them are conditioned on HAVE_CUSTOME_USER_THREADS.** The pragma suppresses the warning.***/#pragma warning(disable : 4101)#endif/* _pr_activeLock protects the following global variables */PRLock*_pr_activeLock;PRInt32_pr_primordialExitCount;/* In PR_Cleanup(), the primordial thread * waits until all other user (non-system) * threads have terminated before it exits. * So whenever we decrement _pr_userActive, * it is compared with * _pr_primordialExitCount. * If the primordial thread is a system * thread, then _pr_primordialExitCount * is 0. If the primordial thread is * itself a user thread, then * _pr_primordialThread is 1. */PRCondVar*_pr_primordialExitCVar;/* When _pr_userActive is decremented to * _pr_primordialExitCount, this condition * variable is notified. */PRLock*_pr_deadQLock;PRUint32_pr_numNativeDead;PRUint32_pr_numUserDead;PRCList_pr_deadNativeQ;PRCList_pr_deadUserQ;PRUint32_pr_join_counter;PRUint32_pr_local_threads;PRUint32_pr_global_threads;PRBoolsuspendAllOn=PR_FALSE;PRThread*suspendAllThread=NULL;externPRCList_pr_active_global_threadQ;externPRCList_pr_active_local_threadQ;staticvoid_PR_DecrActiveThreadCount(PRThread*thread);staticPRThread*_PR_AttachThread(PRThreadType,PRThreadPriority,PRThreadStack*);staticvoid_PR_InitializeNativeStack(PRThreadStack*ts);staticvoid_PR_InitializeRecycledThread(PRThread*thread);staticvoid_PR_UserRunThread(void);void_PR_InitThreads(PRThreadTypetype,PRThreadPrioritypriority,PRUintnmaxPTDs){PRThread*thread;PRThreadStack*stack;PR_ASSERT(priority==PR_PRIORITY_NORMAL);_pr_terminationCVLock=PR_NewLock();_pr_activeLock=PR_NewLock();#ifndef HAVE_CUSTOM_USER_THREADSstack=PR_NEWZAP(PRThreadStack);#ifdef HAVE_STACK_GROWING_UPstack->stackTop=(char*)((((PRWord)&type)>>_pr_pageShift)<<_pr_pageShift);#else#if defined(SOLARIS) || defined (UNIXWARE) && defined (USR_SVR4_THREADS)stack->stackTop=(char*)&thread;#elsestack->stackTop=(char*)((((PRWord)&type+_pr_pageSize-1)>>_pr_pageShift)<<_pr_pageShift);#endif#endif#else/* If stack is NULL, we're using custom user threads like NT fibers. */stack=PR_NEWZAP(PRThreadStack);if(stack){stack->stackSize=0;_PR_InitializeNativeStack(stack);}#endif /* HAVE_CUSTOM_USER_THREADS */thread=_PR_AttachThread(type,priority,stack);if(thread){_PR_MD_SET_CURRENT_THREAD(thread);if(type==PR_SYSTEM_THREAD){thread->flags=_PR_SYSTEM;_pr_systemActive++;_pr_primordialExitCount=0;}else{_pr_userActive++;_pr_primordialExitCount=1;}thread->no_sched=1;_pr_primordialExitCVar=PR_NewCondVar(_pr_activeLock);}if(!thread){PR_Abort();}#ifdef _PR_LOCAL_THREADS_ONLYthread->flags|=_PR_PRIMORDIAL;#elsethread->flags|=_PR_PRIMORDIAL|_PR_GLOBAL_SCOPE;#endif/* * Needs _PR_PRIMORDIAL flag set before calling * _PR_MD_INIT_THREAD() */if(_PR_MD_INIT_THREAD(thread)==PR_FAILURE){/* * XXX do what? */}if(_PR_IS_NATIVE_THREAD(thread)){PR_APPEND_LINK(&thread->active,&_PR_ACTIVE_GLOBAL_THREADQ());_pr_global_threads++;}else{PR_APPEND_LINK(&thread->active,&_PR_ACTIVE_LOCAL_THREADQ());_pr_local_threads++;}_pr_recycleThreads=0;_pr_deadQLock=PR_NewLock();_pr_numNativeDead=0;_pr_numUserDead=0;PR_INIT_CLIST(&_pr_deadNativeQ);PR_INIT_CLIST(&_pr_deadUserQ);}void_PR_CleanupThreads(void){if(_pr_terminationCVLock){PR_DestroyLock(_pr_terminationCVLock);_pr_terminationCVLock=NULL;}if(_pr_activeLock){PR_DestroyLock(_pr_activeLock);_pr_activeLock=NULL;}if(_pr_primordialExitCVar){PR_DestroyCondVar(_pr_primordialExitCVar);_pr_primordialExitCVar=NULL;}/* TODO _pr_dead{Native,User}Q need to be deleted */if(_pr_deadQLock){PR_DestroyLock(_pr_deadQLock);_pr_deadQLock=NULL;}}/*** Initialize a stack for a native thread*/staticvoid_PR_InitializeNativeStack(PRThreadStack*ts){if(ts&&(ts->stackTop==0)){ts->allocSize=ts->stackSize;/* ** Setup stackTop and stackBottom values. */#ifdef HAVE_STACK_GROWING_UPts->allocBase=(char*)((((PRWord)&ts)>>_pr_pageShift)<<_pr_pageShift);ts->stackBottom=ts->allocBase+ts->stackSize;ts->stackTop=ts->allocBase;#elsets->allocBase=(char*)((((PRWord)&ts+_pr_pageSize-1)>>_pr_pageShift)<<_pr_pageShift);ts->stackTop=ts->allocBase;ts->stackBottom=ts->allocBase-ts->stackSize;#endif}}void_PR_NotifyJoinWaiters(PRThread*thread){/* ** Handle joinable threads. Change the state to waiting for join. ** Remove from our run Q and put it on global waiting to join Q. ** Notify on our "termination" condition variable so that joining ** thread will know about our termination. Switch our context and ** come back later on to continue the cleanup. */PR_ASSERT(thread==_PR_MD_CURRENT_THREAD());if(thread->term!=NULL){PR_Lock(_pr_terminationCVLock);_PR_THREAD_LOCK(thread);thread->state=_PR_JOIN_WAIT;if(!_PR_IS_NATIVE_THREAD(thread)){_PR_MISCQ_LOCK(thread->cpu);_PR_ADD_JOINQ(thread,thread->cpu);_PR_MISCQ_UNLOCK(thread->cpu);}_PR_THREAD_UNLOCK(thread);PR_NotifyCondVar(thread->term);PR_Unlock(_pr_terminationCVLock);_PR_MD_WAIT(thread,PR_INTERVAL_NO_TIMEOUT);PR_ASSERT(thread->state!=_PR_JOIN_WAIT);}}/* * Zero some of the data members of a recycled thread. * * Note that we can do this either when a dead thread is added to * the dead thread queue or when it is reused. Here, we are doing * this lazily, when the thread is reused in _PR_CreateThread(). */staticvoid_PR_InitializeRecycledThread(PRThread*thread){/* * Assert that the following data members are already zeroed * by _PR_CleanupThread(). */#ifdef DEBUGif(thread->privateData){unsignedinti;for(i=0;i<thread->tpdLength;i++){PR_ASSERT(thread->privateData[i]==NULL);}}#endifPR_ASSERT(thread->dumpArg==0&&thread->dump==0);PR_ASSERT(thread->errorString==0&&thread->errorStringSize==0);PR_ASSERT(thread->errorStringLength==0);PR_ASSERT(thread->name==0);/* Reset data members in thread structure */thread->errorCode=thread->osErrorCode=0;thread->io_pending=thread->io_suspended=PR_FALSE;thread->environment=0;PR_INIT_CLIST(&thread->lockList);}PRStatus_PR_RecycleThread(PRThread*thread){if(_PR_IS_NATIVE_THREAD(thread)&&_PR_NUM_DEADNATIVE<_pr_recycleThreads){_PR_DEADQ_LOCK;PR_APPEND_LINK(&thread->links,&_PR_DEADNATIVEQ);_PR_INC_DEADNATIVE;_PR_DEADQ_UNLOCK;return(PR_SUCCESS);}elseif(!_PR_IS_NATIVE_THREAD(thread)&&_PR_NUM_DEADUSER<_pr_recycleThreads){_PR_DEADQ_LOCK;PR_APPEND_LINK(&thread->links,&_PR_DEADUSERQ);_PR_INC_DEADUSER;_PR_DEADQ_UNLOCK;return(PR_SUCCESS);}return(PR_FAILURE);}/* * Decrement the active thread count, either _pr_systemActive or * _pr_userActive, depending on whether the thread is a system thread * or a user thread. If all the user threads, except possibly * the primordial thread, have terminated, we notify the primordial * thread of this condition. * * Since this function will lock _pr_activeLock, do not call this * function while holding the _pr_activeLock lock, as this will result * in a deadlock. */staticvoid_PR_DecrActiveThreadCount(PRThread*thread){PR_Lock(_pr_activeLock);if(thread->flags&_PR_SYSTEM){_pr_systemActive--;}else{_pr_userActive--;if(_pr_userActive==_pr_primordialExitCount){PR_NotifyCondVar(_pr_primordialExitCVar);}}PR_Unlock(_pr_activeLock);}/*** Detach thread structure*/staticvoid_PR_DestroyThread(PRThread*thread){_PR_MD_FREE_LOCK(&thread->threadLock);PR_DELETE(thread);}void_PR_NativeDestroyThread(PRThread*thread){if(thread->term){PR_DestroyCondVar(thread->term);thread->term=0;}if(NULL!=thread->privateData){PR_ASSERT(0!=thread->tpdLength);PR_DELETE(thread->privateData);thread->tpdLength=0;}PR_DELETE(thread->stack);_PR_DestroyThread(thread);}void_PR_UserDestroyThread(PRThread*thread){if(thread->term){PR_DestroyCondVar(thread->term);thread->term=0;}if(NULL!=thread->privateData){PR_ASSERT(0!=thread->tpdLength);PR_DELETE(thread->privateData);thread->tpdLength=0;}_PR_MD_FREE_LOCK(&thread->threadLock);if(thread->threadAllocatedOnStack==1){_PR_MD_CLEAN_THREAD(thread);/* * Because the no_sched field is set, this thread/stack will * will not be re-used until the flag is cleared by the thread * we will context switch to. */_PR_FreeStack(thread->stack);}else{#ifdef WINNT_PR_MD_CLEAN_THREAD(thread);#else/* * This assertion does not apply to NT. On NT, every fiber * has its threadAllocatedOnStack equal to 0. Elsewhere, * only the primordial thread has its threadAllocatedOnStack * equal to 0. */PR_ASSERT(thread->flags&_PR_PRIMORDIAL);#endif}}/*** Run a thread's start function. When the start function returns the** thread is done executing and no longer needs the CPU. If there are no** more user threads running then we can exit the program.*/void_PR_NativeRunThread(void*arg){PRThread*thread=(PRThread*)arg;_PR_MD_SET_CURRENT_THREAD(thread);_PR_MD_SET_CURRENT_CPU(NULL);/* Set up the thread stack information */_PR_InitializeNativeStack(thread->stack);/* Set up the thread md information */if(_PR_MD_INIT_THREAD(thread)==PR_FAILURE){/* * thread failed to initialize itself, possibly due to * failure to allocate per-thread resources */return;}while(1){thread->state=_PR_RUNNING;/* * Add to list of active threads */PR_Lock(_pr_activeLock);PR_APPEND_LINK(&thread->active,&_PR_ACTIVE_GLOBAL_THREADQ());_pr_global_threads++;PR_Unlock(_pr_activeLock);(*thread->startFunc)(thread->arg);/* * The following two assertions are meant for NT asynch io. * * The thread should have no asynch io in progress when it * exits, otherwise the overlapped buffer, which is part of * the thread structure, would become invalid. */PR_ASSERT(thread->io_pending==PR_FALSE);/* * This assertion enforces the programming guideline that * if an io function times out or is interrupted, the thread * should close the fd to force the asynch io to abort * before it exits. Right now, closing the fd is the only * way to clear the io_suspended flag. */PR_ASSERT(thread->io_suspended==PR_FALSE);/* * remove thread from list of active threads */PR_Lock(_pr_activeLock);PR_REMOVE_LINK(&thread->active);_pr_global_threads--;PR_Unlock(_pr_activeLock);PR_LOG(_pr_thread_lm,PR_LOG_MIN,("thread exiting"));/* All done, time to go away */_PR_CleanupThread(thread);_PR_NotifyJoinWaiters(thread);_PR_DecrActiveThreadCount(thread);thread->state=_PR_DEAD_STATE;if(!_pr_recycleThreads||(_PR_RecycleThread(thread)==PR_FAILURE)){/* * thread not recycled * platform-specific thread exit processing * - for stuff like releasing native-thread resources, etc. */_PR_MD_EXIT_THREAD(thread);/* * Free memory allocated for the thread */_PR_NativeDestroyThread(thread);/* * thread gone, cannot de-reference thread now */return;}/* Now wait for someone to activate us again... */_PR_MD_WAIT(thread,PR_INTERVAL_NO_TIMEOUT);}}staticvoid_PR_UserRunThread(void){PRThread*thread=_PR_MD_CURRENT_THREAD();PRIntnis;if(_MD_LAST_THREAD()){_MD_LAST_THREAD()->no_sched=0;}#ifdef HAVE_CUSTOM_USER_THREADSif(thread->stack==NULL){thread->stack=PR_NEWZAP(PRThreadStack);_PR_InitializeNativeStack(thread->stack);}#endif /* HAVE_CUSTOM_USER_THREADS */while(1){/* Run thread main */if(!_PR_IS_NATIVE_THREAD(thread)){_PR_MD_SET_INTSOFF(0);}/* * Add to list of active threads */if(!(thread->flags&_PR_IDLE_THREAD)){PR_Lock(_pr_activeLock);PR_APPEND_LINK(&thread->active,&_PR_ACTIVE_LOCAL_THREADQ());_pr_local_threads++;PR_Unlock(_pr_activeLock);}(*thread->startFunc)(thread->arg);/* * The following two assertions are meant for NT asynch io. * * The thread should have no asynch io in progress when it * exits, otherwise the overlapped buffer, which is part of * the thread structure, would become invalid. */PR_ASSERT(thread->io_pending==PR_FALSE);/* * This assertion enforces the programming guideline that * if an io function times out or is interrupted, the thread * should close the fd to force the asynch io to abort * before it exits. Right now, closing the fd is the only * way to clear the io_suspended flag. */PR_ASSERT(thread->io_suspended==PR_FALSE);PR_Lock(_pr_activeLock);/* * remove thread from list of active threads */if(!(thread->flags&_PR_IDLE_THREAD)){PR_REMOVE_LINK(&thread->active);_pr_local_threads--;}PR_Unlock(_pr_activeLock);PR_LOG(_pr_thread_lm,PR_LOG_MIN,("thread exiting"));/* All done, time to go away */_PR_CleanupThread(thread);_PR_INTSOFF(is);_PR_NotifyJoinWaiters(thread);_PR_DecrActiveThreadCount(thread);thread->state=_PR_DEAD_STATE;if(!_pr_recycleThreads||(_PR_RecycleThread(thread)==PR_FAILURE)){/* ** Destroy the thread resources */_PR_UserDestroyThread(thread);}/* ** Find another user thread to run. This cpu has finished the ** previous threads main and is now ready to run another thread. */{PRInt32is;_PR_INTSOFF(is);_PR_MD_SWITCH_CONTEXT(thread);}/* Will land here when we get scheduled again if we are recycling... */}}void_PR_SetThreadPriority(PRThread*thread,PRThreadPrioritynewPri){PRThread*me=_PR_MD_CURRENT_THREAD();PRIntnis;if(_PR_IS_NATIVE_THREAD(thread)){_PR_MD_SET_PRIORITY(&(thread->md),newPri);return;}if(!_PR_IS_NATIVE_THREAD(me)){_PR_INTSOFF(is);}_PR_THREAD_LOCK(thread);if(newPri!=thread->priority){_PRCPU*cpu=thread->cpu;switch(thread->state){case_PR_RUNNING:/* Change my priority */_PR_RUNQ_LOCK(cpu);thread->priority=newPri;if(_PR_RUNQREADYMASK(cpu)>>(newPri+1)){if(!_PR_IS_NATIVE_THREAD(me)){_PR_SET_RESCHED_FLAG();}}_PR_RUNQ_UNLOCK(cpu);break;case_PR_RUNNABLE:_PR_RUNQ_LOCK(cpu);/* Move to different runQ */_PR_DEL_RUNQ(thread);thread->priority=newPri;PR_ASSERT(!(thread->flags&_PR_IDLE_THREAD));_PR_ADD_RUNQ(thread,cpu,newPri);_PR_RUNQ_UNLOCK(cpu);if(newPri>me->priority){if(!_PR_IS_NATIVE_THREAD(me)){_PR_SET_RESCHED_FLAG();}}break;case_PR_LOCK_WAIT:case_PR_COND_WAIT:case_PR_IO_WAIT:case_PR_SUSPENDED:thread->priority=newPri;break;}}_PR_THREAD_UNLOCK(thread);if(!_PR_IS_NATIVE_THREAD(me)){_PR_INTSON(is);}}/*** Suspend the named thread and copy its gc registers into regBuf*/staticvoid_PR_Suspend(PRThread*thread){PRIntnis;PRThread*me=_PR_MD_CURRENT_THREAD();PR_ASSERT(thread!=me);PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread)||(!thread->cpu));if(!_PR_IS_NATIVE_THREAD(me)){_PR_INTSOFF(is);}_PR_THREAD_LOCK(thread);switch(thread->state){case_PR_RUNNABLE:if(!_PR_IS_NATIVE_THREAD(thread)){_PR_RUNQ_LOCK(thread->cpu);_PR_DEL_RUNQ(thread);_PR_RUNQ_UNLOCK(thread->cpu);_PR_MISCQ_LOCK(thread->cpu);_PR_ADD_SUSPENDQ(thread,thread->cpu);_PR_MISCQ_UNLOCK(thread->cpu);}else{/* * Only LOCAL threads are suspended by _PR_Suspend */PR_ASSERT(0);}thread->state=_PR_SUSPENDED;break;case_PR_RUNNING:/* * The thread being suspended should be a LOCAL thread with * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state */PR_ASSERT(0);break;case_PR_LOCK_WAIT:case_PR_IO_WAIT:case_PR_COND_WAIT:if(_PR_IS_NATIVE_THREAD(thread)){_PR_MD_SUSPEND_THREAD(thread);}thread->flags|=_PR_SUSPENDING;break;default:PR_Abort();}_PR_THREAD_UNLOCK(thread);if(!_PR_IS_NATIVE_THREAD(me)){_PR_INTSON(is);}}staticvoid_PR_Resume(PRThread*thread){PRThreadPrioritypri;PRIntnis;PRThread*me=_PR_MD_CURRENT_THREAD();if(!_PR_IS_NATIVE_THREAD(me)){_PR_INTSOFF(is);}_PR_THREAD_LOCK(thread);switch(thread->state){case_PR_SUSPENDED:thread->state=_PR_RUNNABLE;thread->flags&=~_PR_SUSPENDING;if(!_PR_IS_NATIVE_THREAD(thread)){_PR_MISCQ_LOCK(thread->cpu);_PR_DEL_SUSPENDQ(thread);_PR_MISCQ_UNLOCK(thread->cpu);pri=thread->priority;_PR_RUNQ_LOCK(thread->cpu);_PR_ADD_RUNQ(thread,thread->cpu,pri);_PR_RUNQ_UNLOCK(thread->cpu);if(pri>_PR_MD_CURRENT_THREAD()->priority){if(!_PR_IS_NATIVE_THREAD(me)){_PR_SET_RESCHED_FLAG();}}}else{PR_ASSERT(0);}break;case_PR_IO_WAIT:case_PR_COND_WAIT:thread->flags&=~_PR_SUSPENDING;/* PR_ASSERT(thread->wait.monitor->stickyCount == 0); */break;case_PR_LOCK_WAIT:{PRLock*wLock=thread->wait.lock;thread->flags&=~_PR_SUSPENDING;_PR_LOCK_LOCK(wLock);if(thread->wait.lock->owner==0){_PR_UnblockLockWaiter(thread->wait.lock);}_PR_LOCK_UNLOCK(wLock);break;}case_PR_RUNNABLE:break;case_PR_RUNNING:/* * The thread being suspended should be a LOCAL thread with * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state */PR_ASSERT(0);break;default:/* * thread should have been in one of the above-listed blocked states * (_PR_JOIN_WAIT, _PR_IO_WAIT, _PR_UNBORN, _PR_DEAD_STATE) */PR_Abort();}_PR_THREAD_UNLOCK(thread);if(!_PR_IS_NATIVE_THREAD(me)){_PR_INTSON(is);}}#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)staticPRThread*get_thread(_PRCPU*cpu,PRBool*wakeup_cpus){PRThread*thread;PRIntnpri;PRUint32r;PRCList*qp;PRIntnpriMin,priMax;_PR_RUNQ_LOCK(cpu);r=_PR_RUNQREADYMASK(cpu);if(r==0){priMin=priMax=PR_PRIORITY_FIRST;}elseif(r==(1<<PR_PRIORITY_NORMAL)){priMin=priMax=PR_PRIORITY_NORMAL;}else{priMin=PR_PRIORITY_FIRST;priMax=PR_PRIORITY_LAST;}thread=NULL;for(pri=priMax;pri>=priMin;pri--){if(r&(1<<pri)){for(qp=_PR_RUNQ(cpu)[pri].next;qp!=&_PR_RUNQ(cpu)[pri];qp=qp->next){thread=_PR_THREAD_PTR(qp);/* * skip non-schedulable threads */PR_ASSERT(!(thread->flags&_PR_IDLE_THREAD));if(thread->no_sched){thread=NULL;/* * Need to wakeup cpus to avoid missing a * runnable thread * Waking up all CPU's need happen only once. */*wakeup_cpus=PR_TRUE;continue;}elseif(thread->flags&_PR_BOUND_THREAD){/* * Thread bound to cpu 0 */thread=NULL;continue;}elseif(thread->io_pending==PR_TRUE){/* * A thread that is blocked for I/O needs to run * on the same cpu on which it was blocked. This is because * the cpu's ioq is accessed without lock protection and scheduling * the thread on a different cpu would preclude this optimization. */thread=NULL;continue;}else{/* Pull thread off of its run queue */_PR_DEL_RUNQ(thread);_PR_RUNQ_UNLOCK(cpu);return(thread);}}}thread=NULL;}_PR_RUNQ_UNLOCK(cpu);return(thread);}#endif /* !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) *//*** Schedule this native thread by finding the highest priority nspr** thread that is ready to run.**** Note- everyone really needs to call _PR_MD_SWITCH_CONTEXT (which calls** PR_Schedule() rather than calling PR_Schedule. Otherwise if there** is initialization required for switching from SWITCH_CONTEXT,** it will not get done!*/void_PR_Schedule(void){PRThread*thread,*me=_PR_MD_CURRENT_THREAD();_PRCPU*cpu=_PR_MD_CURRENT_CPU();PRIntnpri;PRUint32r;PRCList*qp;PRIntnpriMin,priMax;#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)PRBoolwakeup_cpus;#endif/* Interrupts must be disabled */PR_ASSERT(_PR_IS_NATIVE_THREAD(me)||_PR_MD_GET_INTSOFF()!=0);/* Since we are rescheduling, we no longer want to */_PR_CLEAR_RESCHED_FLAG();/* ** Find highest priority thread to run. Bigger priority numbers are ** higher priority threads */_PR_RUNQ_LOCK(cpu);/* * if we are in SuspendAll mode, can schedule only the thread * that called PR_SuspendAll * * The thread may be ready to run now, after completing an I/O * operation, for example */if((thread=suspendAllThread)!=0){if((!(thread->no_sched))&&(thread->state==_PR_RUNNABLE)){/* Pull thread off of its run queue */_PR_DEL_RUNQ(thread);_PR_RUNQ_UNLOCK(cpu);gotofound_thread;}else{thread=NULL;_PR_RUNQ_UNLOCK(cpu);gotoidle_thread;}}r=_PR_RUNQREADYMASK(cpu);if(r==0){priMin=priMax=PR_PRIORITY_FIRST;}elseif(r==(1<<PR_PRIORITY_NORMAL)){priMin=priMax=PR_PRIORITY_NORMAL;}else{priMin=PR_PRIORITY_FIRST;priMax=PR_PRIORITY_LAST;}thread=NULL;for(pri=priMax;pri>=priMin;pri--){if(r&(1<<pri)){for(qp=_PR_RUNQ(cpu)[pri].next;qp!=&_PR_RUNQ(cpu)[pri];qp=qp->next){thread=_PR_THREAD_PTR(qp);/* * skip non-schedulable threads */PR_ASSERT(!(thread->flags&_PR_IDLE_THREAD));if((thread->no_sched)&&(me!=thread)){thread=NULL;continue;}else{/* Pull thread off of its run queue */_PR_DEL_RUNQ(thread);_PR_RUNQ_UNLOCK(cpu);gotofound_thread;}}}thread=NULL;}_PR_RUNQ_UNLOCK(cpu);#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)wakeup_cpus=PR_FALSE;_PR_CPU_LIST_LOCK();for(qp=_PR_CPUQ().next;qp!=&_PR_CPUQ();qp=qp->next){if(cpu!=_PR_CPU_PTR(qp)){if((thread=get_thread(_PR_CPU_PTR(qp),&wakeup_cpus))!=NULL){thread->cpu=cpu;_PR_CPU_LIST_UNLOCK();if(wakeup_cpus==PR_TRUE){_PR_MD_WAKEUP_CPUS();}gotofound_thread;}}}_PR_CPU_LIST_UNLOCK();if(wakeup_cpus==PR_TRUE){_PR_MD_WAKEUP_CPUS();}#endif /* _PR_LOCAL_THREADS_ONLY */idle_thread:/* ** There are no threads to run. Switch to the idle thread */PR_LOG(_pr_sched_lm,PR_LOG_MAX,("pausing"));thread=_PR_MD_CURRENT_CPU()->idle_thread;found_thread:PR_ASSERT((me==thread)||((thread->state==_PR_RUNNABLE)&&(!(thread->no_sched))));/* Resume the thread */PR_LOG(_pr_sched_lm,PR_LOG_MAX,("switching to %d[%p]",thread->id,thread));PR_ASSERT(thread->state!=_PR_RUNNING);thread->state=_PR_RUNNING;/* If we are on the runq, it just means that we went to sleep on some * resource, and by the time we got here another real native thread had * already given us the resource and put us back on the runqueue */PR_ASSERT(thread->cpu==_PR_MD_CURRENT_CPU());if(thread!=me){_PR_MD_RESTORE_CONTEXT(thread);}#if 0 /* XXXMB; with setjmp/longjmp it is impossible to land here, but * it is not with fibers... Is this a bad thing? I believe it is * still safe. */ PR_NOT_REACHED("impossible return from schedule");#endif}/*** Attaches a thread.** Does not set the _PR_MD_CURRENT_THREAD.** Does not specify the scope of the thread.*/staticPRThread*_PR_AttachThread(PRThreadTypetype,PRThreadPrioritypriority,PRThreadStack*stack){PRThread*thread;char*mem;if(priority>PR_PRIORITY_LAST){priority=PR_PRIORITY_LAST;}elseif(priority<PR_PRIORITY_FIRST){priority=PR_PRIORITY_FIRST;}mem=(char*)PR_CALLOC(sizeof(PRThread));if(mem){thread=(PRThread*)mem;thread->priority=priority;thread->stack=stack;thread->state=_PR_RUNNING;PR_INIT_CLIST(&thread->lockList);if(_PR_MD_NEW_LOCK(&thread->threadLock)==PR_FAILURE){PR_DELETE(thread);return0;}returnthread;}return0;}PR_IMPLEMENT(PRThread*)_PR_NativeCreateThread(PRThreadTypetype,void(*start)(void*arg),void*arg,PRThreadPrioritypriority,PRThreadScopescope,PRThreadStatestate,PRUint32stackSize,PRUint32flags){PRThread*thread;thread=_PR_AttachThread(type,priority,NULL);if(thread){PR_Lock(_pr_activeLock);thread->flags=(flags|_PR_GLOBAL_SCOPE);thread->id=++_pr_utid;if(type==PR_SYSTEM_THREAD){thread->flags|=_PR_SYSTEM;_pr_systemActive++;}else{_pr_userActive++;}PR_Unlock(_pr_activeLock);thread->stack=PR_NEWZAP(PRThreadStack);if(!thread->stack){PR_SetError(PR_OUT_OF_MEMORY_ERROR,0);gotodone;}thread->stack->stackSize=stackSize?stackSize:_MD_DEFAULT_STACK_SIZE;thread->stack->thr=thread;thread->startFunc=start;thread->arg=arg;/* Set thread flags related to scope and joinable state. If joinable thread, allocate a "termination" conidition variable. */if(state==PR_JOINABLE_THREAD){thread->term=PR_NewCondVar(_pr_terminationCVLock);if(thread->term==NULL){PR_DELETE(thread->stack);gotodone;}}thread->state=_PR_RUNNING;if(_PR_MD_CREATE_THREAD(thread,_PR_NativeRunThread,priority,scope,state,stackSize)==PR_SUCCESS){returnthread;}if(thread->term){PR_DestroyCondVar(thread->term);thread->term=NULL;}PR_DELETE(thread->stack);}done:if(thread){_PR_DecrActiveThreadCount(thread);_PR_DestroyThread(thread);}returnNULL;}/************************************************************************/PR_IMPLEMENT(PRThread*)_PR_CreateThread(PRThreadTypetype,void(*start)(void*arg),void*arg,PRThreadPrioritypriority,PRThreadScopescope,PRThreadStatestate,PRUint32stackSize,PRUint32flags){PRThread*me;PRThread*thread=NULL;PRThreadStack*stack;char*top;PRIntnis;PRIntnnative=0;PRIntnuseRecycled=0;PRBoolstatus;/* First, pin down the priority. Not all compilers catch passing out of range enum here. If we let bad values thru, priority queues won't work. */if(priority>PR_PRIORITY_LAST){priority=PR_PRIORITY_LAST;}elseif(priority<PR_PRIORITY_FIRST){priority=PR_PRIORITY_FIRST;}if(!_pr_initialized){_PR_ImplicitInitialization();}if(!(flags&_PR_IDLE_THREAD)){me=_PR_MD_CURRENT_THREAD();}#if defined(_PR_GLOBAL_THREADS_ONLY)/* * can create global threads only */if(scope==PR_LOCAL_THREAD){scope=PR_GLOBAL_THREAD;}#endifif(_native_threads_only){scope=PR_GLOBAL_THREAD;}native=(((scope==PR_GLOBAL_THREAD)||(scope==PR_GLOBAL_BOUND_THREAD))&&_PR_IS_NATIVE_THREAD_SUPPORTED());_PR_ADJUST_STACKSIZE(stackSize);if(native){/* * clear the IDLE_THREAD flag which applies to LOCAL * threads only */flags&=~_PR_IDLE_THREAD;flags|=_PR_GLOBAL_SCOPE;if(_PR_NUM_DEADNATIVE>0){_PR_DEADQ_LOCK;if(_PR_NUM_DEADNATIVE==0){/* Thread safe check */_PR_DEADQ_UNLOCK;}else{thread=_PR_THREAD_PTR(_PR_DEADNATIVEQ.next);PR_REMOVE_LINK(&thread->links);_PR_DEC_DEADNATIVE;_PR_DEADQ_UNLOCK;_PR_InitializeRecycledThread(thread);thread->startFunc=start;thread->arg=arg;thread->flags=(flags|_PR_GLOBAL_SCOPE);if(type==PR_SYSTEM_THREAD){thread->flags|=_PR_SYSTEM;PR_ATOMIC_INCREMENT(&_pr_systemActive);}else{PR_ATOMIC_INCREMENT(&_pr_userActive);}if(state==PR_JOINABLE_THREAD){if(!thread->term){thread->term=PR_NewCondVar(_pr_terminationCVLock);}}else{if(thread->term){PR_DestroyCondVar(thread->term);thread->term=0;}}thread->priority=priority;_PR_MD_SET_PRIORITY(&(thread->md),priority);/* XXX what about stackSize? */thread->state=_PR_RUNNING;_PR_MD_WAKEUP_WAITER(thread);returnthread;}}thread=_PR_NativeCreateThread(type,start,arg,priority,scope,state,stackSize,flags);}else{if(_PR_NUM_DEADUSER>0){_PR_DEADQ_LOCK;if(_PR_NUM_DEADUSER==0){/* thread safe check */_PR_DEADQ_UNLOCK;}else{PRCList*ptr;/* Go down list checking for a recycled thread with a * large enough stack. XXXMB - this has a bad degenerate case. */ptr=_PR_DEADUSERQ.next;while(ptr!=&_PR_DEADUSERQ){thread=_PR_THREAD_PTR(ptr);if((thread->stack->stackSize>=stackSize)&&(!thread->no_sched)){PR_REMOVE_LINK(&thread->links);_PR_DEC_DEADUSER;break;}else{ptr=ptr->next;thread=NULL;}}_PR_DEADQ_UNLOCK;if(thread){_PR_InitializeRecycledThread(thread);thread->startFunc=start;thread->arg=arg;thread->priority=priority;if(state==PR_JOINABLE_THREAD){if(!thread->term){thread->term=PR_NewCondVar(_pr_terminationCVLock);}}else{if(thread->term){PR_DestroyCondVar(thread->term);thread->term=0;}}useRecycled++;}}}if(thread==NULL){#ifndef HAVE_CUSTOM_USER_THREADSstack=_PR_NewStack(stackSize);if(!stack){PR_SetError(PR_OUT_OF_MEMORY_ERROR,0);returnNULL;}/* Allocate thread object and per-thread data off the top of the stack*/top=stack->stackTop;#ifdef HAVE_STACK_GROWING_UPthread=(PRThread*)top;top=top+sizeof(PRThread);/* * Make stack 64-byte aligned */if((PRUptrdiff)top&0x3f){top=(char*)(((PRUptrdiff)top+0x40)&~0x3f);}#elsetop=top-sizeof(PRThread);thread=(PRThread*)top;/* * Make stack 64-byte aligned */if((PRUptrdiff)top&0x3f){top=(char*)((PRUptrdiff)top&~0x3f);}#endifstack->thr=thread;memset(thread,0,sizeof(PRThread));thread->threadAllocatedOnStack=1;#elsethread=_PR_MD_CREATE_USER_THREAD(stackSize,start,arg);if(!thread){PR_SetError(PR_OUT_OF_MEMORY_ERROR,0);returnNULL;}thread->threadAllocatedOnStack=0;stack=NULL;top=NULL;#endif/* Initialize thread */thread->tpdLength=0;thread->privateData=NULL;thread->stack=stack;thread->priority=priority;thread->startFunc=start;thread->arg=arg;PR_INIT_CLIST(&thread->lockList);if(_PR_MD_INIT_THREAD(thread)==PR_FAILURE){if(thread->threadAllocatedOnStack==1){_PR_FreeStack(thread->stack);}else{PR_DELETE(thread);}PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR,0);returnNULL;}if(_PR_MD_NEW_LOCK(&thread->threadLock)==PR_FAILURE){if(thread->threadAllocatedOnStack==1){_PR_FreeStack(thread->stack);}else{PR_DELETE(thread->privateData);PR_DELETE(thread);}PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR,0);returnNULL;}_PR_MD_INIT_CONTEXT(thread,top,_PR_UserRunThread,&status);if(status==PR_FALSE){_PR_MD_FREE_LOCK(&thread->threadLock);if(thread->threadAllocatedOnStack==1){_PR_FreeStack(thread->stack);}else{PR_DELETE(thread->privateData);PR_DELETE(thread);}returnNULL;}/* Set thread flags related to scope and joinable state. If joinable thread, allocate a "termination" condition variable. */if(state==PR_JOINABLE_THREAD){thread->term=PR_NewCondVar(_pr_terminationCVLock);if(thread->term==NULL){_PR_MD_FREE_LOCK(&thread->threadLock);if(thread->threadAllocatedOnStack==1){_PR_FreeStack(thread->stack);}else{PR_DELETE(thread->privateData);PR_DELETE(thread);}returnNULL;}}}/* Update thread type counter */PR_Lock(_pr_activeLock);thread->flags=flags;thread->id=++_pr_utid;if(type==PR_SYSTEM_THREAD){thread->flags|=_PR_SYSTEM;_pr_systemActive++;}else{_pr_userActive++;}/* Make thread runnable */thread->state=_PR_RUNNABLE;/* * Add to list of active threads */PR_Unlock(_pr_activeLock);if((!(thread->flags&_PR_IDLE_THREAD))&&_PR_IS_NATIVE_THREAD(me)){thread->cpu=_PR_GetPrimordialCPU();}else{thread->cpu=_PR_MD_CURRENT_CPU();}PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));if((!(thread->flags&_PR_IDLE_THREAD))&&!_PR_IS_NATIVE_THREAD(me)){_PR_INTSOFF(is);_PR_RUNQ_LOCK(thread->cpu);_PR_ADD_RUNQ(thread,thread->cpu,priority);_PR_RUNQ_UNLOCK(thread->cpu);}if(thread->flags&_PR_IDLE_THREAD){/* ** If the creating thread is a kernel thread, we need to ** awaken the user thread idle thread somehow; potentially ** it could be sleeping in its idle loop, and we need to poke ** it. To do so, wake the idle thread... */_PR_MD_WAKEUP_WAITER(NULL);}elseif(_PR_IS_NATIVE_THREAD(me)){_PR_MD_WAKEUP_WAITER(thread);}if((!(thread->flags&_PR_IDLE_THREAD))&&!_PR_IS_NATIVE_THREAD(me)){_PR_INTSON(is);}}returnthread;}PR_IMPLEMENT(PRThread*)PR_CreateThread(PRThreadTypetype,void(*start)(void*arg),void*arg,PRThreadPrioritypriority,PRThreadScopescope,PRThreadStatestate,PRUint32stackSize){return_PR_CreateThread(type,start,arg,priority,scope,state,stackSize,0);}/*** Associate a thread object with an existing native thread.** "type" is the type of thread object to attach** "priority" is the priority to assign to the thread** "stack" defines the shape of the threads stack**** This can return NULL if some kind of error occurs, or if memory is** tight.**** This call is not normally needed unless you create your own native** thread. PR_Init does this automatically for the primordial thread.*/PRThread*_PRI_AttachThread(PRThreadTypetype,PRThreadPrioritypriority,PRThreadStack*stack,PRUint32flags){PRThread*thread;if((thread=_PR_MD_GET_ATTACHED_THREAD())!=NULL){returnthread;}_PR_MD_SET_CURRENT_THREAD(NULL);/* Clear out any state if this thread was attached before */_PR_MD_SET_CURRENT_CPU(NULL);thread=_PR_AttachThread(type,priority,stack);if(thread){PRIntnis;_PR_MD_SET_CURRENT_THREAD(thread);thread->flags=flags|_PR_GLOBAL_SCOPE|_PR_ATTACHED;if(!stack){thread->stack=PR_NEWZAP(PRThreadStack);if(!thread->stack){_PR_DestroyThread(thread);returnNULL;}thread->stack->stackSize=_MD_DEFAULT_STACK_SIZE;}PR_INIT_CLIST(&thread->links);if(_PR_MD_INIT_ATTACHED_THREAD(thread)==PR_FAILURE){PR_DELETE(thread->stack);_PR_DestroyThread(thread);returnNULL;}_PR_MD_SET_CURRENT_CPU(NULL);if(_PR_MD_CURRENT_CPU()){_PR_INTSOFF(is);PR_Lock(_pr_activeLock);}if(type==PR_SYSTEM_THREAD){thread->flags|=_PR_SYSTEM;_pr_systemActive++;}else{_pr_userActive++;}if(_PR_MD_CURRENT_CPU()){PR_Unlock(_pr_activeLock);_PR_INTSON(is);}}returnthread;}PR_IMPLEMENT(PRThread*)PR_AttachThread(PRThreadTypetype,PRThreadPrioritypriority,PRThreadStack*stack){returnPR_GetCurrentThread();}PR_IMPLEMENT(void)PR_DetachThread(void){/* * On Solaris, and Windows, foreign threads are detached when * they terminate. */#if !defined(WIN32) \ && !(defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY))PRThread*me;if(_pr_initialized){me=_PR_MD_GET_ATTACHED_THREAD();if((me!=NULL)&&(me->flags&_PR_ATTACHED)){_PRI_DetachThread();}}#endif}void_PRI_DetachThread(void){PRThread*me=_PR_MD_CURRENT_THREAD();if(me->flags&_PR_PRIMORDIAL){/* * ignore, if primordial thread */return;}PR_ASSERT(me->flags&_PR_ATTACHED);PR_ASSERT(_PR_IS_NATIVE_THREAD(me));_PR_CleanupThread(me);PR_DELETE(me->privateData);_PR_DecrActiveThreadCount(me);_PR_MD_CLEAN_THREAD(me);_PR_MD_SET_CURRENT_THREAD(NULL);if(!me->threadAllocatedOnStack){PR_DELETE(me->stack);}_PR_MD_FREE_LOCK(&me->threadLock);PR_DELETE(me);}/*** Wait for thread termination:** "thread" is the target thread**** This can return PR_FAILURE if no joinable thread could be found** corresponding to the specified target thread.**** The calling thread is suspended until the target thread completes.** Several threads cannot wait for the same thread to complete; one thread** will complete successfully and others will terminate with an error PR_FAILURE.** The calling thread will not be blocked if the target thread has already** terminated.*/PR_IMPLEMENT(PRStatus)PR_JoinThread(PRThread*thread){PRIntnis;PRCondVar*term;PRThread*me=_PR_MD_CURRENT_THREAD();if(!_PR_IS_NATIVE_THREAD(me)){_PR_INTSOFF(is);}term=thread->term;/* can't join a non-joinable thread */if(term==NULL){PR_SetError(PR_INVALID_ARGUMENT_ERROR,0);gotoErrorExit;}/* multiple threads can't wait on the same joinable thread */if(term->condQ.next!=&term->condQ){gotoErrorExit;}if(!_PR_IS_NATIVE_THREAD(me)){_PR_INTSON(is);}/* wait for the target thread's termination cv invariant */PR_Lock(_pr_terminationCVLock);while(thread->state!=_PR_JOIN_WAIT){(void)PR_WaitCondVar(term,PR_INTERVAL_NO_TIMEOUT);}(void)PR_Unlock(_pr_terminationCVLock);/* Remove target thread from global waiting to join Q; make it runnable again and put it back on its run Q. When it gets scheduled later in _PR_RunThread code, it will clean up its stack. */if(!_PR_IS_NATIVE_THREAD(me)){_PR_INTSOFF(is);}thread->state=_PR_RUNNABLE;if(!_PR_IS_NATIVE_THREAD(thread)){_PR_THREAD_LOCK(thread);_PR_MISCQ_LOCK(thread->cpu);_PR_DEL_JOINQ(thread);_PR_MISCQ_UNLOCK(thread->cpu);_PR_AddThreadToRunQ(me,thread);_PR_THREAD_UNLOCK(thread);}if(!_PR_IS_NATIVE_THREAD(me)){_PR_INTSON(is);}_PR_MD_WAKEUP_WAITER(thread);returnPR_SUCCESS;ErrorExit:if(!_PR_IS_NATIVE_THREAD(me)){_PR_INTSON(is);}returnPR_FAILURE;}PR_IMPLEMENT(void)PR_SetThreadPriority(PRThread*thread,PRThreadPrioritynewPri){/* First, pin down the priority. Not all compilers catch passing out of range enum here. If we let bad values thru, priority queues won't work. */if((PRIntn)newPri>(PRIntn)PR_PRIORITY_LAST){newPri=PR_PRIORITY_LAST;}elseif((PRIntn)newPri<(PRIntn)PR_PRIORITY_FIRST){newPri=PR_PRIORITY_FIRST;}if(_PR_IS_NATIVE_THREAD(thread)){thread->priority=newPri;_PR_MD_SET_PRIORITY(&(thread->md),newPri);}else{_PR_SetThreadPriority(thread,newPri);}}PR_IMPLEMENT(PRStatus)PR_SetCurrentThreadName(constchar*name){PRThread*thread;size_tnameLen;if(!name){PR_SetError(PR_INVALID_ARGUMENT_ERROR,0);returnPR_FAILURE;}thread=PR_GetCurrentThread();if(!thread){returnPR_FAILURE;}PR_Free(thread->name);nameLen=strlen(name);thread->name=(char*)PR_Malloc(nameLen+1);if(!thread->name){returnPR_FAILURE;}memcpy(thread->name,name,nameLen+1);_PR_MD_SET_CURRENT_THREAD_NAME(thread->name);returnPR_SUCCESS;}PR_IMPLEMENT(constchar*)PR_GetThreadName(constPRThread*thread){if(!thread){returnNULL;}returnthread->name;}/*** This routine prevents all other threads from running. This call is needed by** the garbage collector.*/PR_IMPLEMENT(void)PR_SuspendAll(void){PRThread*me=_PR_MD_CURRENT_THREAD();PRCList*qp;/* * Stop all user and native threads which are marked GC able. */PR_Lock(_pr_activeLock);suspendAllOn=PR_TRUE;suspendAllThread=_PR_MD_CURRENT_THREAD();_PR_MD_BEGIN_SUSPEND_ALL();for(qp=_PR_ACTIVE_LOCAL_THREADQ().next;qp!=&_PR_ACTIVE_LOCAL_THREADQ();qp=qp->next){if((me!=_PR_ACTIVE_THREAD_PTR(qp))&&_PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))){_PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp));PR_ASSERT((_PR_ACTIVE_THREAD_PTR(qp))->state!=_PR_RUNNING);}}for(qp=_PR_ACTIVE_GLOBAL_THREADQ().next;qp!=&_PR_ACTIVE_GLOBAL_THREADQ();qp=qp->next){if((me!=_PR_ACTIVE_THREAD_PTR(qp))&&_PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp)))/* PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); */{_PR_MD_SUSPEND_THREAD(_PR_ACTIVE_THREAD_PTR(qp));}}_PR_MD_END_SUSPEND_ALL();}/*** This routine unblocks all other threads that were suspended from running by** PR_SuspendAll(). This call is needed by the garbage collector.*/PR_IMPLEMENT(void)PR_ResumeAll(void){PRThread*me=_PR_MD_CURRENT_THREAD();PRCList*qp;/* * Resume all user and native threads which are marked GC able. */_PR_MD_BEGIN_RESUME_ALL();for(qp=_PR_ACTIVE_LOCAL_THREADQ().next;qp!=&_PR_ACTIVE_LOCAL_THREADQ();qp=qp->next){if((me!=_PR_ACTIVE_THREAD_PTR(qp))&&_PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))){_PR_Resume(_PR_ACTIVE_THREAD_PTR(qp));}}for(qp=_PR_ACTIVE_GLOBAL_THREADQ().next;qp!=&_PR_ACTIVE_GLOBAL_THREADQ();qp=qp->next){if((me!=_PR_ACTIVE_THREAD_PTR(qp))&&_PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))){_PR_MD_RESUME_THREAD(_PR_ACTIVE_THREAD_PTR(qp));}}_PR_MD_END_RESUME_ALL();suspendAllThread=NULL;suspendAllOn=PR_FALSE;PR_Unlock(_pr_activeLock);}PR_IMPLEMENT(PRStatus)PR_EnumerateThreads(PREnumeratorfunc,void*arg){PRCList*qp,*qp_next;PRIntni=0;PRStatusrv=PR_SUCCESS;PRThread*t;/* ** Currently Enumerate threads happen only with suspension and ** pr_activeLock held */PR_ASSERT(suspendAllOn);/* Steve Morse, 4-23-97: Note that we can't walk a queue by taking * qp->next after applying the function "func". In particular, "func" * might remove the thread from the queue and put it into another one in * which case qp->next no longer points to the next entry in the original * queue. * * To get around this problem, we save qp->next in qp_next before applying * "func" and use that saved value as the next value after applying "func". *//* * Traverse the list of local and global threads */for(qp=_PR_ACTIVE_LOCAL_THREADQ().next;qp!=&_PR_ACTIVE_LOCAL_THREADQ();qp=qp_next){qp_next=qp->next;t=_PR_ACTIVE_THREAD_PTR(qp);if(_PR_IS_GCABLE_THREAD(t)){rv=(*func)(t,i,arg);if(rv!=PR_SUCCESS){returnrv;}i++;}}for(qp=_PR_ACTIVE_GLOBAL_THREADQ().next;qp!=&_PR_ACTIVE_GLOBAL_THREADQ();qp=qp_next){qp_next=qp->next;t=_PR_ACTIVE_THREAD_PTR(qp);if(_PR_IS_GCABLE_THREAD(t)){rv=(*func)(t,i,arg);if(rv!=PR_SUCCESS){returnrv;}i++;}}returnrv;}/* FUNCTION: _PR_AddSleepQ** DESCRIPTION:** Adds a thread to the sleep/pauseQ.** RESTRICTIONS:** Caller must have the RUNQ lock.** Caller must be a user level thread*/PR_IMPLEMENT(void)_PR_AddSleepQ(PRThread*thread,PRIntervalTimetimeout){_PRCPU*cpu=thread->cpu;if(timeout==PR_INTERVAL_NO_TIMEOUT){/* append the thread to the global pause Q */PR_APPEND_LINK(&thread->links,&_PR_PAUSEQ(thread->cpu));thread->flags|=_PR_ON_PAUSEQ;}else{PRIntervalTimesleep;PRCList*q;PRThread*t;/* sort onto global sleepQ */sleep=timeout;/* Check if we are longest timeout */if(timeout>=_PR_SLEEPQMAX(cpu)){PR_INSERT_BEFORE(&thread->links,&_PR_SLEEPQ(cpu));thread->sleep=timeout-_PR_SLEEPQMAX(cpu);_PR_SLEEPQMAX(cpu)=timeout;}else{/* Sort thread into global sleepQ at appropriate point */q=_PR_SLEEPQ(cpu).next;/* Now scan the list for where to insert this entry */while(q!=&_PR_SLEEPQ(cpu)){t=_PR_THREAD_PTR(q);if(sleep<t->sleep){/* Found sleeper to insert in front of */break;}sleep-=t->sleep;q=q->next;}thread->sleep=sleep;PR_INSERT_BEFORE(&thread->links,q);/* ** Subtract our sleep time from the sleeper that follows us (there ** must be one) so that they remain relative to us. */PR_ASSERT(thread->links.next!=&_PR_SLEEPQ(cpu));t=_PR_THREAD_PTR(thread->links.next);PR_ASSERT(_PR_THREAD_PTR(t->links.prev)==thread);t->sleep-=sleep;}thread->flags|=_PR_ON_SLEEPQ;}}/* FUNCTION: _PR_DelSleepQ** DESCRIPTION:** Removes a thread from the sleep/pauseQ.** INPUTS:** If propogate_time is true, then the thread following the deleted** thread will be get the time from the deleted thread. This is used** when deleting a sleeper that has not timed out.** RESTRICTIONS:** Caller must have the RUNQ lock.** Caller must be a user level thread*/PR_IMPLEMENT(void)_PR_DelSleepQ(PRThread*thread,PRBoolpropogate_time){_PRCPU*cpu=thread->cpu;/* Remove from pauseQ/sleepQ */if(thread->flags&(_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)){if(thread->flags&_PR_ON_SLEEPQ){PRCList*q=thread->links.next;if(q!=&_PR_SLEEPQ(cpu)){if(propogate_time==PR_TRUE){PRThread*after=_PR_THREAD_PTR(q);after->sleep+=thread->sleep;}else{_PR_SLEEPQMAX(cpu)-=thread->sleep;}}else{/* Check if prev is the beggining of the list; if so, * we are the only element on the list. */if(thread->links.prev!=&_PR_SLEEPQ(cpu)){_PR_SLEEPQMAX(cpu)-=thread->sleep;}else{_PR_SLEEPQMAX(cpu)=0;}}thread->flags&=~_PR_ON_SLEEPQ;}else{thread->flags&=~_PR_ON_PAUSEQ;}PR_REMOVE_LINK(&thread->links);}else{PR_ASSERT(0);}}void_PR_AddThreadToRunQ(PRThread*me,/* the current thread */PRThread*thread)/* the local thread to be added to a run queue */{PRThreadPrioritypri=thread->priority;_PRCPU*cpu=thread->cpu;PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));#if defined(WINNT)/* * On NT, we can only reliably know that the current CPU * is not idle. We add the awakened thread to the run * queue of its CPU if its CPU is the current CPU. * For any other CPU, we don't really know whether it * is busy or idle. So in all other cases, we just * "post" the awakened thread to the IO completion port * for the next idle CPU to execute (this is done in * _PR_MD_WAKEUP_WAITER). * Threads with a suspended I/O operation remain bound to * the same cpu until I/O is cancelled * * NOTE: the boolean expression below must be the exact * opposite of the corresponding boolean expression in * _PR_MD_WAKEUP_WAITER. */if((!_PR_IS_NATIVE_THREAD(me)&&(cpu==me->cpu))||(thread->md.thr_bound_cpu)){PR_ASSERT(!thread->md.thr_bound_cpu||(thread->md.thr_bound_cpu==cpu));_PR_RUNQ_LOCK(cpu);_PR_ADD_RUNQ(thread,cpu,pri);_PR_RUNQ_UNLOCK(cpu);}#else_PR_RUNQ_LOCK(cpu);_PR_ADD_RUNQ(thread,cpu,pri);_PR_RUNQ_UNLOCK(cpu);if(!_PR_IS_NATIVE_THREAD(me)&&(cpu==me->cpu)){if(pri>me->priority){_PR_SET_RESCHED_FLAG();}}#endif}