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); } }
/* ** 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 */