void AsioContext::runUntil(c_WaitableWaitHandle* wait_handle) { assert(!m_current); assert(wait_handle); assert(wait_handle->getContext() == this); auto session = AsioSession::Get(); uint8_t check_ete_counter = 0; if (!session->hasAbruptInterruptException()) { session->initAbruptInterruptException(); } while (!wait_handle->isFinished()) { // process ready external thread events once per 256 other events // (when 8-bit check_ete_counter overflows) if (!++check_ete_counter) { // queue may contain received unprocessed events from failed runUntil() auto queue = session->getExternalThreadEventQueue(); if (UNLIKELY(queue->hasReceived()) || queue->tryReceiveSome()) { queue->processAllReceived(); } } // run queue of ready continuations once if (!m_runnableQueue.empty()) { auto current = m_runnableQueue.front(); m_runnableQueue.pop(); m_current = current; auto exit_guard = folly::makeGuard([&] { m_current = nullptr; decRefObj(current); }); m_current->run(); continue; } // run default priority queue once if (runSingle(m_priorityQueueDefault)) { continue; } // pending external thread events? wait for at least one to become ready if (!m_externalThreadEvents.empty()) { // queue may contain received unprocessed events from failed runUntil() auto queue = session->getExternalThreadEventQueue(); if (LIKELY(!queue->hasReceived())) { // all your wait time are belong to us queue->receiveSome(); } queue->processAllReceived(); continue; } // run no-pending-io priority queue once if (runSingle(m_priorityQueueNoPendingIO)) { continue; } // What? The wait handle did not finish? We know it is part of the current // context and since there is nothing else to run, it cannot be in RUNNING // or SCHEDULED state. So it must be BLOCKED on something. Apparently, the // same logic can be used recursively on the something, so there is an // infinite chain of blocked wait handles. But our memory is not infinite. // What could it possibly mean? I think we are in a deep sh^H^Hcycle. // But we can't, the cycles are detected and avoided at blockOn() time. // So, looks like it's not cycle, but the word I started typing first. assert(false); throw FatalErrorException( "Invariant violation: queues are empty, but wait handle did not finish"); } }
void AsioContext::runUntil(c_WaitableWaitHandle* wait_handle) { assert(wait_handle); assert(wait_handle->getContext() == this); auto session = AsioSession::Get(); auto ete_queue = session->getExternalThreadEventQueue(); auto& sleep_queue = session->getSleepEventQueue(); if (!session->hasAbruptInterruptException()) { session->initAbruptInterruptException(); } while (!wait_handle->isFinished()) { // Run queue of ready async functions once. if (!m_runnableQueue.empty()) { auto current = m_runnableQueue.back(); m_runnableQueue.pop_back(); current->resume(); continue; } // Process all sleep handles that have completed their sleep. if (session->processSleepEvents()) { continue; } // Process all external thread events that have completed their operation. // Queue may contain received unprocessed events from failed runUntil(). if (UNLIKELY(ete_queue->hasReceived()) || ete_queue->tryReceiveSome()) { ete_queue->processAllReceived(); continue; } // Run default priority queue once. if (runSingle(m_priorityQueueDefault)) { continue; } // Wait for pending external thread events... if (!m_externalThreadEvents.empty()) { // ...but only until the next sleeper (from any context) finishes. AsioSession::TimePoint waketime; if (sleep_queue.empty()) { waketime = AsioSession::getLatestWakeTime(); } else { waketime = sleep_queue.top()->getWakeTime(); } // Wait if necessary. if (LIKELY(!ete_queue->hasReceived())) { onIOWaitEnter(session); ete_queue->receiveSomeUntil(waketime); onIOWaitExit(session); } if (ete_queue->hasReceived()) { // Either we didn't have to wait, or we waited but no sleeper timed us // out, so just handle the ETEs. ete_queue->processAllReceived(); } else { // No received events means the next-to-wake sleeper timed us out. session->processSleepEvents(); } continue; } // If we're here, then the only things left are sleepers. Wait for one to // be ready (in any context). if (!m_sleepEvents.empty()) { onIOWaitEnter(session); std::this_thread::sleep_until(sleep_queue.top()->getWakeTime()); onIOWaitExit(session); session->processSleepEvents(); continue; } // Run no-pending-io priority queue once. if (runSingle(m_priorityQueueNoPendingIO)) { continue; } // What? The wait handle did not finish? We know it is part of the current // context and since there is nothing else to run, it cannot be in RUNNING // or SCHEDULED state. So it must be BLOCKED on something. Apparently, the // same logic can be used recursively on the something, so there is an // infinite chain of blocked wait handles. But our memory is not infinite. // What could it possibly mean? I think we are in a deep sh^H^Hcycle. // But we can't, the cycles are detected and avoided at blockOn() time. // So, looks like it's not cycle, but the word I started typing first. assert(false); throw FatalErrorException( "Invariant violation: queues are empty, but wait handle did not finish"); } }