onPacketAcked method

void onPacketAcked(
  1. int bytes,
  2. DateTime sentTime,
  3. Duration ackDelay,
  4. bool isNewCumulativeAck,
  5. int currentFrameLargestAcked,
)

Called when a packet is acknowledged. bytes is the size of the acknowledged packet. sentTime is the time the acknowledged packet was sent. ackDelay is the delay the receiver waited before sending the ACK. isNewCumulativeAck true if the ACK frame advanced the highest cumulative ACK point. currentFrameLargestAcked the 'largest_acked' value from the current ACK frame.

Implementation

void onPacketAcked(int bytes, DateTime sentTime, Duration ackDelay, bool isNewCumulativeAck, int currentFrameLargestAcked) {
  // DIAGNOSTIC LOGGING START
  // //print('[CC onPacketAcked] INPUTS: bytes=$bytes, sentTime=$sentTime, ackDelay=$ackDelay, isNewCumulativeAck=$isNewCumulativeAck, currentFrameLargestAcked=$currentFrameLargestAcked');
  // //print('[CC onPacketAcked] PRE-STATE: _inRecovery=$_inRecovery, _highestProcessedCumulativeAck=$_highestProcessedCumulativeAck, _dupAcks=$_dupAcks, _lastAckedForDupCount=$_lastAckedForDupCount, _cwnd=$_cwnd, _ssthresh=$_ssthresh, _inflight=$_inflight');
  // DIAGNOSTIC LOGGING END

  _inflight -= bytes;
  if (_inflight < 0) _inflight = 0;

  bool cwndChanged = false;
  int oldCwnd = _cwnd;
  bool ssthreshChanged = false;
  int oldSsthresh = _ssthresh;
  bool dupAcksChanged = false;
  int oldDupAcks = _dupAcks;
  bool inRecoveryChanged = false;
  bool oldInRecovery = _inRecovery;


  if (isNewCumulativeAck) {
    // RTT should only be updated when the cumulative ACK point advances.
    _updateRtt(sentTime, ackDelay);

    _highestProcessedCumulativeAck = currentFrameLargestAcked;
    if (_dupAcks != 0) {
      _dupAcks = 0; // Reset dupAcks as cumulative ACK advanced.
      dupAcksChanged = true;
    }
    _lastAckedForDupCount = -1; // Reset since it's a new cumulative ack

    if (_inRecovery) {
      // Exit recovery if the acknowledged packet was sent after the recovery period started.
      if (currentFrameLargestAcked > _recoveryEndPacketNumber) {
        _inRecovery = false;
        inRecoveryChanged = true;
        // //print('[CC] Exiting recovery. CWND remains at ssthresh: $_cwnd');
      }
    }

    if (!_inRecovery) {
      // Congestion control algorithm
      if (_cwnd < _ssthresh) {
        // Slow start
        _cwnd += bytes;
        cwndChanged = true;
      } else {
        // CUBIC Congestion avoidance
        _cubicUpdate(bytes);
        cwndChanged = true;
      }
    }
  } else {
    // This packet was SACKed by an ACK frame that did NOT advance the cumulative point.
    // Or it's an older ACK.
    // Do not reset _dupAcks here. Do not grow _cwnd if in recovery.
    // If not in recovery, SACKs for new data can still contribute to CWND growth
    // if we are not in congestion avoidance due to dupAcks.
    // However, RFC 9002 is more conservative: "During recovery, the congestion window
    // does not increase in response to ACKs." (Section 7.3.3)
    // So, if _inRecovery is true, no CWND increase.
    // If _inRecovery is false, but this is part of a duplicate ACK frame (isNewCumulativeAck = false),
    // it implies we might be getting SACKs while also counting dupAcks.
    // The primary CWND growth is tied to isNewCumulativeAck = true.
    // For SACKs on a duplicate cumulative ACK, we mainly care about RTT and inflight.
  }

  // Disarm PTO timer on successful ACK processing for this packet.
  // //print('[CongestionController] onPacketAcked: Disarming PTO timer.');
  _ptoTimer?.cancel();
  _ptoTimer = null;

  // Reset PTO retry count on successful ACK - connection is working
  _ptoRetryCount = 0;

  // If there are still packets in flight, re-arm the timer.
  if (_inflight > 0) {
    // //print('[CongestionController] onPacketAcked: Re-arming PTO timer, inflight=$_inflight');
    _armPtoTimer();
  }

  // No CWND modification here if !isNewCumulativeAck, as per above logic.
  // DIAGNOSTIC LOGGING START
  // //print('[CC onPacketAcked] POST-STATE: _inRecovery=$_inRecovery (changed: $inRecoveryChanged, old: $oldInRecovery), _highestProcessedCumulativeAck=$_highestProcessedCumulativeAck, _dupAcks=$_dupAcks (changed: $dupAcksChanged, old: $oldDupAcks), _lastAckedForDupCount=$_lastAckedForDupCount, _cwnd=$_cwnd (changed: $cwndChanged, old: $oldCwnd), _ssthresh=$_ssthresh (changed: $ssthreshChanged, old: $oldSsthresh), _inflight=$_inflight');
  // DIAGNOSTIC LOGGING END
}