void
HTTP2PriorityQueue::nextEgress(HTTP2PriorityQueue::NextEgressResult& result) {
  struct WeightCmp {
    bool operator()(const std::pair<HTTPTransaction*, double>& t1,
                    const std::pair<HTTPTransaction*, double>& t2) {
      return t1.second > t2.second;
    }
  };

  result.reserve(activeCount_);
  nextEgressResults_.reset(&result);

  updateEnqueuedWeight();
  Node::PendingList pendingNodes;
  pendingNodes.emplace_back(0, &root_, 1.0);
  bool stop = false;
  while (!stop && !pendingNodes.empty()) {
    Node* node = pendingNodes.front().node;
    if (node) {
      stop = node->visitBFS(pendingNodes.front().ratio, nextEgressResult,
                            false, pendingNodes, true /* enqueued children */);
    }
    pendingNodes.pop_front();
  }
  std::sort(result.begin(), result.end(), WeightCmp());
  nextEgressResults_.release();
}
void
HTTP2PriorityQueue::nextEgress(HTTP2PriorityQueue::NextEgressResult& result,
                               bool spdyMode) {
  struct WeightCmp {
    bool operator()(const std::pair<HTTPTransaction*, double>& t1,
                    const std::pair<HTTPTransaction*, double>& t2) {
      return t1.second > t2.second;
    }
  };

  result.reserve(activeCount_);
  nextEgressResults_.reset(&result);

  updateEnqueuedWeight();
  Node::PendingList pendingNodes;
  Node::PendingList pendingNodesTmp;
  pendingNodes.emplace_back(0, &root_, 1.0);
  bool stop = false;
  do {
    while (!stop && !pendingNodes.empty()) {
      Node* node = pendingNodes.front().node;
      if (node) {
        stop = node->visitBFS(pendingNodes.front().ratio, nextEgressResult,
                              false, pendingNodesTmp,
                              true /* enqueued children */);
      }
      pendingNodes.pop_front();
    }
    // In SPDY mode, we stop as soon one level of the tree produces results,
    // then normalize the ratios.
    if (spdyMode && !result.empty() && !pendingNodesTmp.empty()) {
      double totalRatio = 0;
      for (auto &txnPair: result) {
        totalRatio += txnPair.second;
      }
      CHECK_GT(totalRatio, 0);
      for (auto &txnPair: result) {
        txnPair.second = txnPair.second / totalRatio;
      }
      break;
    }
    std::swap(pendingNodes, pendingNodesTmp);
  } while (!stop && !pendingNodes.empty());
  std::sort(result.begin(), result.end(), WeightCmp());
  nextEgressResults_.release();
}
void
HTTP2PriorityQueue::iterateBFS(
  const std::function<bool(HTTPCodec::StreamID, HTTPTransaction *, double)>& fn,
  const std::function<bool()>& stopFn, bool all) {
  Node::PendingList pendingNodes{{0, &root_, 1.0}};
  Node::PendingList newPendingNodes;
  bool stop = false;

  updateEnqueuedWeight();
  while (!stop && !stopFn() && !pendingNodes.empty()) {
    CHECK(newPendingNodes.empty());
    while (!stop && !pendingNodes.empty()) {
      Node* node = findInternal(pendingNodes.front().id);
      if (node) {
        stop = node->visitBFS(pendingNodes.front().ratio, fn, all,
                              newPendingNodes, false /* all children */);
      }
      pendingNodes.pop_front();
    }
    std::swap(pendingNodes, newPendingNodes);
  }
}