/* * 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; }
void _nt_handle_restarted_io(PRThread *restarted_io) { /* After the switch we can resume an IO if needed. * XXXMB - this needs to be done in create thread, since that could * be the result for a context switch too.. */ PR_ASSERT(restarted_io->io_suspended == PR_TRUE); PR_ASSERT(restarted_io->md.thr_bound_cpu == restarted_io->cpu); _PR_THREAD_LOCK(restarted_io); if (restarted_io->io_pending == PR_FALSE) { /* The IO already completed, put us back on the runq. */ int pri = restarted_io->priority; restarted_io->state = _PR_RUNNABLE; _PR_RUNQ_LOCK(restarted_io->cpu); _PR_ADD_RUNQ(restarted_io, restarted_io->cpu, pri); _PR_RUNQ_UNLOCK(restarted_io->cpu); } else { _PR_SLEEPQ_LOCK(restarted_io->cpu); _PR_ADD_SLEEPQ(restarted_io, restarted_io->sleep); _PR_SLEEPQ_UNLOCK(restarted_io->cpu); } restarted_io->io_suspended = PR_FALSE; restarted_io->md.thr_bound_cpu = NULL; _PR_THREAD_UNLOCK(restarted_io); if (_pr_use_static_tls) { _pr_io_restarted_io = NULL; } else { TlsSetValue(_pr_io_restartedIOIndex, NULL); } }
/* ** 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); } }
/* ** Make the given thread wait for the given condition variable */ PRStatus _PR_WaitCondVar( PRThread *thread, PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout) { PRIntn is; PRStatus rv = PR_SUCCESS; PR_ASSERT(thread == _PR_MD_CURRENT_THREAD()); PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); #ifdef _PR_GLOBAL_THREADS_ONLY if (_PR_PENDING_INTERRUPT(thread)) { PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); thread->flags &= ~_PR_INTERRUPT; return PR_FAILURE; } thread->wait.cvar = cvar; lock->owner = NULL; _PR_MD_WAIT_CV(&cvar->md,&lock->ilock, timeout); thread->wait.cvar = NULL; lock->owner = thread; if (_PR_PENDING_INTERRUPT(thread)) { PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); thread->flags &= ~_PR_INTERRUPT; return PR_FAILURE; } return PR_SUCCESS; #else /* _PR_GLOBAL_THREADS_ONLY */ if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_INTSOFF(is); _PR_CVAR_LOCK(cvar); _PR_THREAD_LOCK(thread); if (_PR_PENDING_INTERRUPT(thread)) { PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); thread->flags &= ~_PR_INTERRUPT; _PR_CVAR_UNLOCK(cvar); _PR_THREAD_UNLOCK(thread); if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_INTSON(is); return PR_FAILURE; } thread->state = _PR_COND_WAIT; thread->wait.cvar = cvar; /* ** Put the caller thread on the condition variable's wait Q */ PR_APPEND_LINK(&thread->waitQLinks, &cvar->condQ); /* Note- for global scope threads, we don't put them on the * global sleepQ, so each global thread must put itself * to sleep only for the time it wants to. */ if ( !_PR_IS_NATIVE_THREAD(thread) ) { _PR_SLEEPQ_LOCK(thread->cpu); _PR_ADD_SLEEPQ(thread, timeout); _PR_SLEEPQ_UNLOCK(thread->cpu); } _PR_CVAR_UNLOCK(cvar); _PR_THREAD_UNLOCK(thread); /* ** Release lock protecting the condition variable and thereby giving time ** to the next thread which can potentially notify on the condition variable */ PR_Unlock(lock); PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_Wait: cvar=%p waiting for %d", cvar, timeout)); rv = _PR_MD_WAIT(thread, timeout); _PR_CVAR_LOCK(cvar); PR_REMOVE_LINK(&thread->waitQLinks); _PR_CVAR_UNLOCK(cvar); PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_Wait: cvar=%p done waiting", cvar)); if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_INTSON(is); /* Acquire lock again that we had just relinquished */ PR_Lock(lock); if (_PR_PENDING_INTERRUPT(thread)) { PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); thread->flags &= ~_PR_INTERRUPT; return PR_FAILURE; } return rv; #endif /* _PR_GLOBAL_THREADS_ONLY */ }
PR_IMPLEMENT(PRRecvWait*) PR_WaitRecvReady(PRWaitGroup *group) { PRCList *io_ready = NULL; #ifdef WINNT PRThread *me = _PR_MD_CURRENT_THREAD(); _MDOverlapped *overlapped; #endif if (!_pr_initialized) _PR_ImplicitInitialization(); if ((NULL == group) && (NULL == (group = MW_Init2()))) goto failed_init; PR_Lock(group->ml); if (_prmw_running != group->state) { PR_SetError(PR_INVALID_STATE_ERROR, 0); goto invalid_state; } group->waiting_threads += 1; /* the polling thread is counted */ #ifdef WINNT _PR_MD_LOCK(&group->mdlock); while (PR_CLIST_IS_EMPTY(&group->io_ready)) { _PR_THREAD_LOCK(me); me->state = _PR_IO_WAIT; PR_APPEND_LINK(&me->waitQLinks, &group->wait_list); if (!_PR_IS_NATIVE_THREAD(me)) { _PR_SLEEPQ_LOCK(me->cpu); _PR_ADD_SLEEPQ(me, PR_INTERVAL_NO_TIMEOUT); _PR_SLEEPQ_UNLOCK(me->cpu); } _PR_THREAD_UNLOCK(me); _PR_MD_UNLOCK(&group->mdlock); PR_Unlock(group->ml); _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT); me->state = _PR_RUNNING; PR_Lock(group->ml); _PR_MD_LOCK(&group->mdlock); if (_PR_PENDING_INTERRUPT(me)) { PR_REMOVE_LINK(&me->waitQLinks); _PR_MD_UNLOCK(&group->mdlock); me->flags &= ~_PR_INTERRUPT; me->io_suspended = PR_FALSE; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); goto aborted; } } io_ready = PR_LIST_HEAD(&group->io_ready); PR_ASSERT(io_ready != NULL); PR_REMOVE_LINK(io_ready); _PR_MD_UNLOCK(&group->mdlock); overlapped = (_MDOverlapped *) ((char *)io_ready - offsetof(_MDOverlapped, data)); io_ready = &overlapped->data.mw.desc->internal; #else do { /* ** If the I/O ready list isn't empty, have this thread ** return with the first receive wait object that's available. */ if (PR_CLIST_IS_EMPTY(&group->io_ready)) { /* ** Is there a polling thread yet? If not, grab this thread ** and use it. */ if (NULL == group->poller) { /* ** This thread will stay do polling until it becomes the only one ** left to service a completion. Then it will return and there will ** be none left to actually poll or to run completions. ** ** The polling function should only return w/ failure or ** with some I/O ready. */ if (PR_FAILURE == _MW_PollInternal(group)) goto failed_poll; } else { /* ** There are four reasons a thread can be awakened from ** a wait on the io_complete condition variable. ** 1. Some I/O has completed, i.e., the io_ready list ** is nonempty. ** 2. The wait group is canceled. ** 3. The thread is interrupted. ** 4. The current polling thread has to leave and needs ** a replacement. ** The logic to find a new polling thread is made more ** complicated by all the other possible events. ** I tried my best to write the logic clearly, but ** it is still full of if's with continue and goto. */ PRStatus st; do { st = PR_WaitCondVar(group->io_complete, PR_INTERVAL_NO_TIMEOUT); if (_prmw_running != group->state) { PR_SetError(PR_INVALID_STATE_ERROR, 0); goto aborted; } if (_MW_ABORTED(st) || (NULL == group->poller)) break; } while (PR_CLIST_IS_EMPTY(&group->io_ready)); /* ** The thread is interrupted and has to leave. It might ** have also been awakened to process ready i/o or be the ** new poller. To be safe, if either condition is true, ** we awaken another thread to take its place. */ if (_MW_ABORTED(st)) { if ((NULL == group->poller || !PR_CLIST_IS_EMPTY(&group->io_ready)) && group->waiting_threads > 1) PR_NotifyCondVar(group->io_complete); goto aborted; } /* ** A new poller is needed, but can I be the new poller? ** If there is no i/o ready, sure. But if there is any ** i/o ready, it has a higher priority. I want to ** process the ready i/o first and wake up another ** thread to be the new poller. */ if (NULL == group->poller) { if (PR_CLIST_IS_EMPTY(&group->io_ready)) continue; if (group->waiting_threads > 1) PR_NotifyCondVar(group->io_complete); } } PR_ASSERT(!PR_CLIST_IS_EMPTY(&group->io_ready)); } io_ready = PR_LIST_HEAD(&group->io_ready); PR_NotifyCondVar(group->io_taken); PR_ASSERT(io_ready != NULL); PR_REMOVE_LINK(io_ready); } while (NULL == io_ready); failed_poll: #endif aborted: group->waiting_threads -= 1; invalid_state: (void)MW_TestForShutdownInternal(group); PR_Unlock(group->ml); failed_init: if (NULL != io_ready) { /* If the operation failed, record the reason why */ switch (((PRRecvWait*)io_ready)->outcome) { case PR_MW_PENDING: PR_ASSERT(0); break; case PR_MW_SUCCESS: #ifndef WINNT _MW_InitialRecv(io_ready); #endif break; #ifdef WINNT case PR_MW_FAILURE: _PR_MD_MAP_READ_ERROR(overlapped->data.mw.error); break; #endif case PR_MW_TIMEOUT: PR_SetError(PR_IO_TIMEOUT_ERROR, 0); break; case PR_MW_INTERRUPT: PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); break; default: break; } #ifdef WINNT if (NULL != overlapped->data.mw.timer) { PR_ASSERT(PR_INTERVAL_NO_TIMEOUT != overlapped->data.mw.desc->timeout); CancelTimer(overlapped->data.mw.timer); } else { PR_ASSERT(PR_INTERVAL_NO_TIMEOUT == overlapped->data.mw.desc->timeout); } PR_DELETE(overlapped); #endif } return (PRRecvWait*)io_ready; } /* PR_WaitRecvReady */
PR_IMPLEMENT(PRRecvWait*) PR_CancelWaitGroup(PRWaitGroup *group) { PRRecvWait **desc; PRRecvWait *recv_wait = NULL; #ifdef WINNT _MDOverlapped *overlapped; PRRecvWait **end; PRThread *me = _PR_MD_CURRENT_THREAD(); #endif if (NULL == group) group = mw_state->group; PR_ASSERT(NULL != group); if (NULL == group) { PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); return NULL; } PR_Lock(group->ml); if (_prmw_stopped != group->state) { if (_prmw_running == group->state) group->state = _prmw_stopping; /* so nothing new comes in */ if (0 == group->waiting_threads) /* is there anybody else? */ group->state = _prmw_stopped; /* we can stop right now */ else { PR_NotifyAllCondVar(group->new_business); PR_NotifyAllCondVar(group->io_complete); } while (_prmw_stopped != group->state) (void)PR_WaitCondVar(group->mw_manage, PR_INTERVAL_NO_TIMEOUT); } #ifdef WINNT _PR_MD_LOCK(&group->mdlock); #endif /* make all the existing descriptors look done/interrupted */ #ifdef WINNT end = &group->waiter->recv_wait + group->waiter->length; for (desc = &group->waiter->recv_wait; desc < end; ++desc) { if (NULL != *desc) { if (InterlockedCompareExchange((LONG *)&(*desc)->outcome, (LONG)PR_MW_INTERRUPT, (LONG)PR_MW_PENDING) == (LONG)PR_MW_PENDING) { PRFileDesc *bottom = PR_GetIdentitiesLayer( (*desc)->fd, PR_NSPR_IO_LAYER); PR_ASSERT(NULL != bottom); if (NULL == bottom) { PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); goto invalid_arg; } bottom->secret->state = _PR_FILEDESC_CLOSED; #if 0 fprintf(stderr, "cancel wait group: closing socket\n"); #endif if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR) { fprintf(stderr, "closesocket failed: %d\n", WSAGetLastError()); exit(1); } } } } while (group->waiter->count > 0) { _PR_THREAD_LOCK(me); me->state = _PR_IO_WAIT; PR_APPEND_LINK(&me->waitQLinks, &group->wait_list); if (!_PR_IS_NATIVE_THREAD(me)) { _PR_SLEEPQ_LOCK(me->cpu); _PR_ADD_SLEEPQ(me, PR_INTERVAL_NO_TIMEOUT); _PR_SLEEPQ_UNLOCK(me->cpu); } _PR_THREAD_UNLOCK(me); _PR_MD_UNLOCK(&group->mdlock); PR_Unlock(group->ml); _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT); me->state = _PR_RUNNING; PR_Lock(group->ml); _PR_MD_LOCK(&group->mdlock); } #else for (desc = &group->waiter->recv_wait; group->waiter->count > 0; ++desc) { PR_ASSERT(desc < &group->waiter->recv_wait + group->waiter->length); if (NULL != *desc) _MW_DoneInternal(group, desc, PR_MW_INTERRUPT); } #endif /* take first element of finished list and return it or NULL */ if (PR_CLIST_IS_EMPTY(&group->io_ready)) PR_SetError(PR_GROUP_EMPTY_ERROR, 0); else { PRCList *head = PR_LIST_HEAD(&group->io_ready); PR_REMOVE_AND_INIT_LINK(head); #ifdef WINNT overlapped = (_MDOverlapped *) ((char *)head - offsetof(_MDOverlapped, data)); head = &overlapped->data.mw.desc->internal; if (NULL != overlapped->data.mw.timer) { PR_ASSERT(PR_INTERVAL_NO_TIMEOUT != overlapped->data.mw.desc->timeout); CancelTimer(overlapped->data.mw.timer); } else { PR_ASSERT(PR_INTERVAL_NO_TIMEOUT == overlapped->data.mw.desc->timeout); } PR_DELETE(overlapped); #endif recv_wait = (PRRecvWait*)head; } #ifdef WINNT invalid_arg: _PR_MD_UNLOCK(&group->mdlock); #endif PR_Unlock(group->ml); return recv_wait; } /* PR_CancelWaitGroup */