void Cubic::onPacketAckedInHystart()

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;
    }
  }
}