in quic/congestion_control/QuicCubic.cpp [413:537]
void Cubic::onPacketAckedInHystart(const AckEvent& ack) {
if (!hystartState_.inRttRound) {
startHystartRttRound(ack.ackTime);
}
// TODO: Should we not increase cwnd if inflight is less than half of cwnd?
// Note that we take bytes out of inflightBytes before invoke the state
// machine. So the inflightBytes here is already reduced.
if (std::numeric_limits<decltype(cwndBytes_)>::max() - cwndBytes_ <
ack.ackedBytes) {
throw QuicInternalException(
"Cubic Hystart: cwnd overflow", LocalErrorCode::CWND_OVERFLOW);
}
VLOG(15) << "Cubic Hystart increase cwnd=" << cwndBytes_ << ", by "
<< ack.ackedBytes;
cwndBytes_ = boundedCwnd(
cwndBytes_ + ack.ackedBytes,
conn_.udpSendPacketLen,
conn_.transportSettings.maxCwndInMss,
conn_.transportSettings.minCwndInMss);
folly::Optional<Cubic::ExitReason> exitReason;
SCOPE_EXIT {
if (hystartState_.found != Cubic::HystartFound::No &&
cwndBytes_ >= kLowSsthreshInMss * conn_.udpSendPacketLen) {
exitReason = Cubic::ExitReason::EXITPOINT;
}
if (exitReason.has_value()) {
VLOG(15) << "Cubic exit slow start, reason = "
<< (*exitReason == Cubic::ExitReason::SSTHRESH
? "cwnd > ssthresh"
: "found exit point");
hystartState_.inRttRound = false;
ssthresh_ = cwndBytes_;
/* Now we exit slow start, reset currSampledRtt to be maximal value so
* that next time we go back to slow start, we won't be using a very old
* sampled RTT as the lastSampledRtt:
*/
hystartState_.currSampledRtt = folly::none;
steadyState_.lastMaxCwndBytes = folly::none;
steadyState_.lastReductionTime = folly::none;
quiescenceStart_ = folly::none;
state_ = CubicStates::Steady;
} else {
// No exit yet, but we may still need to end this RTT round
VLOG(20) << "Cubic Hystart, mayEndHystartRttRound, largestAckedPacketNum="
<< *ack.largestNewlyAckedPacket << ", rttRoundEndTarget="
<< hystartState_.rttRoundEndTarget.time_since_epoch().count();
if (ack.largestNewlyAckedPacketSentTime >
hystartState_.rttRoundEndTarget) {
hystartState_.inRttRound = false;
}
}
};
if (cwndBytes_ >= ssthresh_) {
exitReason = Cubic::ExitReason::SSTHRESH;
return;
}
DCHECK_LE(cwndBytes_, ssthresh_);
if (hystartState_.found != Cubic::HystartFound::No) {
return;
}
if (hystartState_.ackTrain) {
hystartState_.delayMin = std::min(
hystartState_.delayMin.value_or(conn_.lossState.srtt),
conn_.lossState.srtt);
// Within kAckCountingGap since lastJiffy:
// TODO: we should experiment with subtract ackdelay from
// (ackTime - lastJiffy) as well
if (ack.ackTime - hystartState_.lastJiffy <= kAckCountingGap) {
hystartState_.lastJiffy = ack.ackTime;
if ((ack.ackTime - hystartState_.roundStart) * 2 >=
hystartState_.delayMin.value()) {
hystartState_.found = Cubic::HystartFound::FoundByAckTrainMethod;
}
}
}
// If AckTrain wasn't used or didn't find the exit point, continue with
// DelayIncrease.
if (hystartState_.found == Cubic::HystartFound::No) {
if (hystartState_.ackCount < kAckSampling) {
hystartState_.currSampledRtt = std::min(
conn_.lossState.srtt,
hystartState_.currSampledRtt.value_or(conn_.lossState.srtt));
// We can return early if ++ackCount not meeting kAckSampling:
if (++hystartState_.ackCount < kAckSampling) {
VLOG(20) << "Cubic, AckTrain didn't find exit point. ackCount also "
<< "smaller than kAckSampling. Return early";
return;
}
}
if (!hystartState_.lastSampledRtt.has_value() ||
(*hystartState_.lastSampledRtt >=
std::chrono::microseconds::max() - delayIncreaseLowerBound)) {
return;
}
auto eta = std::min(
delayIncreaseUpperBound,
std::max(
delayIncreaseLowerBound,
std::chrono::microseconds(
hystartState_.lastSampledRtt.value().count() >> 4)));
// lastSampledRtt + eta may overflow:
if (*hystartState_.lastSampledRtt >
std::chrono::microseconds::max() - eta) {
// No way currSampledRtt can top this either, return
// TODO: so our rtt is within 8us (kDelayIncreaseUpperBound) of the
// microseconds::max(), should we just shut down the connection?
return;
}
VLOG(20) << "Cubic Hystart: looking for DelayIncrease, with eta="
<< eta.count() << "us, currSampledRtt="
<< hystartState_.currSampledRtt.value().count()
<< "us, lastSampledRtt="
<< hystartState_.lastSampledRtt.value().count()
<< "us, ackCount=" << (uint32_t)hystartState_.ackCount;
if (hystartState_.ackCount >= kAckSampling &&
*hystartState_.currSampledRtt >= *hystartState_.lastSampledRtt + eta) {
hystartState_.found = Cubic::HystartFound::FoundByDelayIncreaseMethod;
}
}
}