void HTTPTransaction::sendEOM() { DestructorGuard g(this); CHECK(HTTPTransactionEgressSM::transit( egressState_, HTTPTransactionEgressSM::Event::sendEOM)) << ", " << *this; if (deferredEgressBody_.chainLength() == 0 && chunkHeaders_.empty()) { // there is nothing left to send, egress the EOM directly. For SPDY // this will jump the txn queue if (!isEnqueued()) { size_t nbytes = sendEOMNow(); transport_.notifyPendingEgress(); if (transportCallback_) { transportCallback_->bodyBytesGenerated(nbytes); } } else { // If the txn is enqueued, sendDeferredBody() // should take care of sending the EOM. // Nevertheless we never expect this condition to occur, // so, log. LOG(ERROR) << "Queued egress EOM with no body on " << *this << "[egressState=" << egressState_ << ", " << "ingressState=" << ingressState_ << ", " << "egressPaused=" << egressPaused_ << ", " << "ingressPaused=" << ingressPaused_ << ", " << "aborted=" << aborted_ << ", " << "enqueued=" << isEnqueued() << ", " << "chainLength=" << deferredEgressBody_.chainLength() << "]"; } } else { VLOG(4) << "Queued egress EOM on " << *this; notifyTransportPendingEgress(); } }
bool HTTPTransaction::onWriteReady(const uint32_t maxEgress, double ratio) { DestructorGuard g(this); DCHECK(isEnqueued()); cumulativeRatio_ += ratio; egressCalls_++; sendDeferredBody(maxEgress); return isEnqueued(); }
void HTTPTransaction::markEgressComplete() { VLOG(4) << "Marking egress complete on " << *this; if (deferredEgressBody_.chainLength() && isEnqueued()) { int64_t deferredEgressBodyBytes = folly::to<int64_t>(deferredEgressBody_.chainLength()); transport_.notifyEgressBodyBuffered(-deferredEgressBodyBytes); } deferredEgressBody_.move(); if (isEnqueued()) { dequeue(); } egressState_ = HTTPTransactionEgressSM::State::SendingDone; }
// Removes the node from the tree void HTTP2PriorityQueue::Node::removeFromTree() { if (!children_.empty()) { // update child weights so they sum to (approximately) this node's weight. double r = double(weight_) / totalChildWeight_; for (auto& child: children_) { uint64_t newWeight = std::max(uint64_t(child->weight_ * r), uint64_t(1)); CHECK_LE(newWeight, 256); child->updateWeight(uint8_t(newWeight) - 1); } } CHECK(!isEnqueued()); if (inEgressTree()) { // Gah this is tricky. // The children of this node are moving to this node's parent. We need the // tree in a consistent state before calling addChildren, so mark the // current node's totalEnqueuedWeight_ as 0 and propagate the clear upwards. // addChildren will handle re-signalling egress. totalEnqueuedWeight_ = 0; propagatePendingEgressClear(this); } // move my children to my parent parent_->addChildren(std::move(children_)); (void)parent_->detachChild(this); }
void CLMatrixLoader::waitEndOfEvaluation() { if ( !isEnqueued() ) { throw EvaluationProcessViolation( "Cannot wait for evaluation as it is not enqued."); } else { getEndOfEvaluation().wait(); } }
// Set a new weight for this node void HTTP2PriorityQueue::Node::updateWeight(uint8_t weight) { int16_t delta = weight - weight_ + 1; weight_ = weight + 1; parent_->totalChildWeight_ += delta; if (isEnqueued()) { parent_->totalEnqueuedWeight_ += delta; } }
void HTTPTransaction::sendBody(std::unique_ptr<folly::IOBuf> body) { DestructorGuard guard(this); CHECK(HTTPTransactionEgressSM::transit( egressState_, HTTPTransactionEgressSM::Event::sendBody)); if (body && isEnqueued()) { size_t bodyLen = body->computeChainDataLength(); transport_.notifyEgressBodyBuffered(bodyLen); } deferredEgressBody_.append(std::move(body)); notifyTransportPendingEgress(); }
void HTTPTransaction::notifyTransportPendingEgress() { DestructorGuard guard(this); if (!egressRateLimited_ && (deferredEgressBody_.chainLength() > 0 || isEgressEOMQueued()) && (!useFlowControl_ || sendWindow_.getSize() > 0)) { // Egress isn't paused, we have something to send, and flow // control isn't blocking us. if (!isEnqueued()) { // Insert into the queue and let the session know we've got something egressQueue_.signalPendingEgress(queueHandle_); transport_.notifyPendingEgress(); transport_.notifyEgressBodyBuffered(deferredEgressBody_.chainLength()); } } else if (isEnqueued()) { // Nothing to send, or not allowed to send right now. int64_t deferredEgressBodyBytes = folly::to<int64_t>(deferredEgressBody_.chainLength()); transport_.notifyEgressBodyBuffered(-deferredEgressBodyBytes); egressQueue_.clearPendingEgress(queueHandle_); } updateHandlerPauseState(); }
HTTPTransaction::~HTTPTransaction() { // Cancel transaction timeout if still scheduled. if (isScheduled()) { cancelTimeout(); } if (stats_) { stats_->recordTransactionClosed(); } if (isEnqueued()) { dequeue(); } // TODO: handle the case where the priority node hangs out longer than // the transaction egressQueue_.removeTransaction(queueHandle_); }
void HTTP2PriorityQueue::Node::updateEnqueuedWeight(bool activeNodes) { totalEnqueuedWeightCheck_ = totalChildWeight_; for (auto& child: children_) { child->updateEnqueuedWeight(activeNodes); } if (activeNodes) { if (totalEnqueuedWeightCheck_ == 0 && !isEnqueued()) { // Must only be called with activeCount_ > 0, root cannot be dequeued CHECK_NOTNULL(parent_); parent_->totalEnqueuedWeightCheck_ -= weight_; } else { CHECK(parent_ == nullptr || enqueuedHook_.is_linked()); } } else { totalEnqueuedWeightCheck_ = 0; } }
void HTTPTransaction::onDelayedDestroy(bool delayed) { if (!isEgressComplete() || !isIngressComplete() || isEnqueued() || deleting_) { return; } VLOG(4) << "destroying transaction " << *this; deleting_ = true; if (handler_) { handler_->detachTransaction(); handler_ = nullptr; } transportCallback_ = nullptr; const auto bytesBuffered = recvWindow_.getOutstanding(); if (bytesBuffered) { transport_.notifyIngressBodyProcessed(bytesBuffered); } transport_.detach(this); (void)delayed; // prevent unused variable warnings }
bool HTTP2PriorityQueue::Node::visitBFS( double relativeParentWeight, const std::function<bool(HTTP2PriorityQueue& queue, HTTPCodec::StreamID, HTTPTransaction *, double)>& fn, bool all, PendingList& pendingNodes, bool enqueuedChildren) { bool invoke = (parent_ != nullptr && (all || isEnqueued())); auto relativeEnqueuedWeight = getRelativeEnqueuedWeight(); #ifndef NDEBUG CHECK_EQ(totalEnqueuedWeight_, totalEnqueuedWeightCheck_); #endif // Add children when all==true, or for any not invoked node with // pending children if (all || (!invoke && totalEnqueuedWeight_ > 0)) { double newRelWeight = relativeParentWeight * relativeEnqueuedWeight; if (enqueuedChildren) { for (auto child = enqueuedChildren_.begin(); child != enqueuedChildren_.end(); child++) { pendingNodes.emplace_back(child->id_, &(*child), newRelWeight); } } else { for (auto& child: children_) { pendingNodes.emplace_back(child->id_, child.get(), newRelWeight); } } } // Invoke fn last in case it deletes this node if (invoke && fn(queue_, id_, txn_, relativeParentWeight * relativeEnqueuedWeight)) { return true; } return false; }
bool HTTP2PriorityQueue::Node::iterate( const std::function<bool(HTTPCodec::StreamID, HTTPTransaction *, double)>& fn, const std::function<bool()>& stopFn, bool all) { bool stop = false; if (stopFn()) { return true; } #ifndef NDEBUG CHECK_EQ(totalEnqueuedWeight_, totalEnqueuedWeightCheck_); #endif if (parent_ /* exclude root */ && (all || isEnqueued())) { stop = fn(id_, txn_, getRelativeWeight()); } for (auto& child: children_) { if (stop || stopFn()) { return true; } stop = child->iterate(fn, stopFn, all); } return stop; }