/* * Notify thread waiting on cvar; called when thread is interrupted * The thread lock is held on entry and released before return */ void _PR_NotifyLockedThread (PRThread *thread) { PRThread *me = _PR_MD_CURRENT_THREAD(); PRCondVar *cvar; PRThreadPriority pri; if ( !_PR_IS_NATIVE_THREAD(me)) PR_ASSERT(_PR_MD_GET_INTSOFF() != 0); cvar = thread->wait.cvar; thread->wait.cvar = NULL; _PR_THREAD_UNLOCK(thread); _PR_CVAR_LOCK(cvar); _PR_THREAD_LOCK(thread); if (!_PR_IS_NATIVE_THREAD(thread)) { _PR_SLEEPQ_LOCK(thread->cpu); /* The notify and timeout can collide; in which case both may * attempt to delete from the sleepQ; only let one do it. */ if (thread->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) _PR_DEL_SLEEPQ(thread, PR_TRUE); _PR_SLEEPQ_UNLOCK(thread->cpu); /* Make thread runnable */ pri = thread->priority; thread->state = _PR_RUNNABLE; PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); _PR_AddThreadToRunQ(me, thread); _PR_THREAD_UNLOCK(thread); _PR_MD_WAKEUP_WAITER(thread); } else { if (thread->flags & _PR_SUSPENDING) { /* * set thread state to SUSPENDED; a Resume operation * on the thread will enable the thread to run */ thread->state = _PR_SUSPENDED; } else thread->state = _PR_RUNNING; _PR_THREAD_UNLOCK(thread); _PR_MD_WAKEUP_WAITER(thread); } _PR_CVAR_UNLOCK(cvar); return; }
/* ** Unblock the first runnable waiting thread. Skip over ** threads that are trying to be suspended ** Note: Caller must hold _PR_LOCK_LOCK() */ void _PR_UnblockLockWaiter(PRLock *lock) { PRThread *t = NULL; PRThread *me; PRCList *q; q = lock->waitQ.next; PR_ASSERT(q != &lock->waitQ); while (q != &lock->waitQ) { /* Unblock first waiter */ t = _PR_THREAD_CONDQ_PTR(q); /* ** We are about to change the thread's state to runnable and for local ** threads, we are going to assign a cpu to it. So, protect thread's ** data structure. */ _PR_THREAD_LOCK(t); if (t->flags & _PR_SUSPENDING) { q = q->next; _PR_THREAD_UNLOCK(t); continue; } /* Found a runnable thread */ PR_ASSERT(t->state == _PR_LOCK_WAIT); PR_ASSERT(t->wait.lock == lock); t->wait.lock = 0; PR_REMOVE_LINK(&t->waitQLinks); /* take it off lock's waitQ */ /* ** If this is a native thread, nothing else to do except to wake it ** up by calling the machine dependent wakeup routine. ** ** If this is a local thread, we need to assign it a cpu and ** put the thread on that cpu's run queue. There are two cases to ** take care of. If the currently running thread is also a local ** thread, we just assign our own cpu to that thread and put it on ** the cpu's run queue. If the the currently running thread is a ** native thread, we assign the primordial cpu to it (on NT, ** MD_WAKEUP handles the cpu assignment). */ if ( !_PR_IS_NATIVE_THREAD(t) ) { t->state = _PR_RUNNABLE; me = _PR_MD_CURRENT_THREAD(); _PR_AddThreadToRunQ(me, t); _PR_THREAD_UNLOCK(t); } else { t->state = _PR_RUNNING; _PR_THREAD_UNLOCK(t); } _PR_MD_WAKEUP_WAITER(t); break; } return; }
/* ** Notify one thread that it has finished waiting on a condition variable ** Caller must hold the _PR_CVAR_LOCK(cv) */ PRBool _PR_NotifyThread (PRThread *thread, PRThread *me) { PRBool rv; PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); _PR_THREAD_LOCK(thread); PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); if ( !_PR_IS_NATIVE_THREAD(thread) ) { if (thread->wait.cvar != NULL) { thread->wait.cvar = NULL; _PR_SLEEPQ_LOCK(thread->cpu); /* The notify and timeout can collide; in which case both may * attempt to delete from the sleepQ; only let one do it. */ if (thread->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) _PR_DEL_SLEEPQ(thread, PR_TRUE); _PR_SLEEPQ_UNLOCK(thread->cpu); if (thread->flags & _PR_SUSPENDING) { /* * set thread state to SUSPENDED; a Resume operation * on the thread will move it to the runQ */ thread->state = _PR_SUSPENDED; _PR_MISCQ_LOCK(thread->cpu); _PR_ADD_SUSPENDQ(thread, thread->cpu); _PR_MISCQ_UNLOCK(thread->cpu); _PR_THREAD_UNLOCK(thread); } else { /* Make thread runnable */ thread->state = _PR_RUNNABLE; _PR_THREAD_UNLOCK(thread); _PR_AddThreadToRunQ(me, thread); _PR_MD_WAKEUP_WAITER(thread); } rv = PR_TRUE; } else { /* Thread has already been notified */ _PR_THREAD_UNLOCK(thread); rv = PR_FALSE; } } else { /* If the thread is a native thread */ if (thread->wait.cvar) { thread->wait.cvar = NULL; if (thread->flags & _PR_SUSPENDING) { /* * set thread state to SUSPENDED; a Resume operation * on the thread will enable the thread to run */ thread->state = _PR_SUSPENDED; } else thread->state = _PR_RUNNING; _PR_THREAD_UNLOCK(thread); _PR_MD_WAKEUP_WAITER(thread); rv = PR_TRUE; } else { _PR_THREAD_UNLOCK(thread); rv = PR_FALSE; } } return rv; }
/* ** Expire condition variable waits that are ready to expire. "now" is the current ** time. */ void _PR_ClockInterrupt(void) { PRThread *thread, *me = _PR_MD_CURRENT_THREAD(); _PRCPU *cpu = me->cpu; PRIntervalTime elapsed, now; PR_ASSERT(_PR_MD_GET_INTSOFF() != 0); /* Figure out how much time elapsed since the last clock tick */ now = PR_IntervalNow(); elapsed = now - cpu->last_clock; cpu->last_clock = now; #ifndef XP_MAC PR_LOG(_pr_clock_lm, PR_LOG_MAX, ("ExpireWaits: elapsed=%lld usec", elapsed)); #endif while(1) { _PR_SLEEPQ_LOCK(cpu); if (_PR_SLEEPQ(cpu).next == &_PR_SLEEPQ(cpu)) { _PR_SLEEPQ_UNLOCK(cpu); break; } thread = _PR_THREAD_PTR(_PR_SLEEPQ(cpu).next); PR_ASSERT(thread->cpu == cpu); if (elapsed < thread->sleep) { thread->sleep -= elapsed; _PR_SLEEPQMAX(thread->cpu) -= elapsed; _PR_SLEEPQ_UNLOCK(cpu); break; } _PR_SLEEPQ_UNLOCK(cpu); PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread)); _PR_THREAD_LOCK(thread); if (thread->cpu != cpu) { /* ** The thread was switched to another CPU ** between the time we unlocked the sleep ** queue and the time we acquired the thread ** lock, so it is none of our business now. */ _PR_THREAD_UNLOCK(thread); continue; } /* ** Consume this sleeper's amount of elapsed time from the elapsed ** time value. The next remaining piece of elapsed time will be ** available for the next sleeping thread's timer. */ _PR_SLEEPQ_LOCK(cpu); PR_ASSERT(!(thread->flags & _PR_ON_PAUSEQ)); if (thread->flags & _PR_ON_SLEEPQ) { _PR_DEL_SLEEPQ(thread, PR_FALSE); elapsed -= thread->sleep; _PR_SLEEPQ_UNLOCK(cpu); } else { /* Thread was already handled; Go get another one */ _PR_SLEEPQ_UNLOCK(cpu); _PR_THREAD_UNLOCK(thread); continue; } /* Notify the thread waiting on the condition variable */ if (thread->flags & _PR_SUSPENDING) { PR_ASSERT((thread->state == _PR_IO_WAIT) || (thread->state == _PR_COND_WAIT)); /* ** Thread is suspended and its condition timeout ** expired. Transfer thread from sleepQ to suspendQ. */ thread->wait.cvar = NULL; _PR_MISCQ_LOCK(cpu); thread->state = _PR_SUSPENDED; _PR_ADD_SUSPENDQ(thread, cpu); _PR_MISCQ_UNLOCK(cpu); } else { if (thread->wait.cvar) { PRThreadPriority pri; /* Do work very similar to what _PR_NotifyThread does */ PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) ); /* Make thread runnable */ pri = thread->priority; thread->state = _PR_RUNNABLE; PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); PR_ASSERT(thread->cpu == cpu); _PR_RUNQ_LOCK(cpu); _PR_ADD_RUNQ(thread, cpu, pri); _PR_RUNQ_UNLOCK(cpu); if (pri > me->priority) _PR_SET_RESCHED_FLAG(); thread->wait.cvar = NULL; _PR_MD_WAKEUP_WAITER(thread); } else if (thread->io_pending == PR_TRUE) { /* Need to put IO sleeper back on runq */ int pri = thread->priority; thread->io_suspended = PR_TRUE; #ifdef WINNT /* * For NT, record the cpu on which I/O was issued * I/O cancellation is done on the same cpu */ thread->md.thr_bound_cpu = cpu; #endif PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); PR_ASSERT(thread->cpu == cpu); thread->state = _PR_RUNNABLE; _PR_RUNQ_LOCK(cpu); _PR_ADD_RUNQ(thread, cpu, pri); _PR_RUNQ_UNLOCK(cpu); } } _PR_THREAD_UNLOCK(thread); } }