void c_ExternalThreadEventWaitHandle::sweep() {
  assert(getState() == STATE_WAITING);

  if (m_event->cancel()) {
    // canceled; the processing thread will take care of cleanup
    return;
  }

  // event has finished, but process() was not called yet
  auto queue = AsioSession::Get()->getExternalThreadEventQueue();
  bool done = queue->hasReceived() && queue->abandonAllReceived(this);
  while (!done) {
    queue->receiveSome();
    done = queue->abandonAllReceived(this);
  }
}
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");
  }
}