📄 pruthr.c
字号:
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(PRThreadType type, PRThreadPriority priority, PRThreadStack *stack, PRUint32 flags){ PRThread *thread; if ((thread = _PR_MD_GET_ATTACHED_THREAD()) != NULL) { return thread; } _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) { PRIntn is; _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); return NULL; } 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); return NULL; } _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); } } return thread;}PR_IMPLEMENT(PRThread*) PR_AttachThread(PRThreadType type, PRThreadPriority priority, PRThreadStack *stack){#ifdef XP_MAC#pragma unused( type, priority, stack )#endif return PR_GetCurrentThread();}PR_IMPLEMENT(void) PR_DetachThread(void){}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){ PRIntn is; 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); goto ErrorExit; } /* multiple threads can't wait on the same joinable thread */ if (term->condQ.next != &term->condQ) { goto ErrorExit; } 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); return PR_SUCCESS;ErrorExit: if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); return PR_FAILURE; }PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thread, PRThreadPriority newPri){ /* 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; } else if ((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);}/*** 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(PREnumerator func, void *arg){ PRCList *qp, *qp_next; PRIntn i = 0; PRStatus rv = 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) return rv; 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) return rv; i++; } } return rv;}/* 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, PRIntervalTime timeout){ _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 { PRIntervalTime sleep; 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, PRBool propogate_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 */{ PRThreadPriority pri = 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}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -