Exemplo n.º 1
0
c_WaitableWaitHandle* AsioContext::getBlamedWaitHandle() {
  // first let's try to find wait handle, responsible for wakeup
  auto session = AsioSession::Get();
  auto ete_queue = session->getExternalThreadEventQueue();

  c_ExternalThreadEventWaitHandle* ewh = ete_queue->lastReceived();
  if (ewh != nullptr &&
      ewh->getContextIdx() == session->getCurrentContextIdx()) {
    return ewh;
  }

  // may return cancelled wait handle, which no longer has contextIdx
  c_SleepWaitHandle* swh = session->nextSleepEvent();
  if (swh != nullptr && !swh->isFinished() &&
      swh->getContextIdx() == session->getCurrentContextIdx() &&
      swh->getWakeTime() <= AsioSession::TimePoint::clock::now()) {
    return swh;
  }

  // not found? let's find a wait handle from current context to blame
  if (!m_externalThreadEvents.empty()) {
    return m_externalThreadEvents[0];
  }

  if (!m_sleepEvents.empty()) {
    return m_sleepEvents[0];
  }

  return nullptr;
}
Exemplo n.º 2
0
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");
  }
}
Exemplo n.º 3
0
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");
  }
}