gfe-relnote: In QUIC, add connection option to enable IETF loss detection with both adaptive packet and adaptive time threshold. Protected by existing gfe2_reloadable_flag_quic_enable_ietf_loss_detection.

PiperOrigin-RevId: 278375594
Change-Id: Ib3dd4aa9208ec367197ec46d5b401c0f390e6085
diff --git a/quic/core/congestion_control/general_loss_algorithm.cc b/quic/core/congestion_control/general_loss_algorithm.cc
index 5f7115a..8798322 100644
--- a/quic/core/congestion_control/general_loss_algorithm.cc
+++ b/quic/core/congestion_control/general_loss_algorithm.cc
@@ -32,6 +32,7 @@
     : loss_detection_timeout_(QuicTime::Zero()),
       reordering_threshold_(kNumberOfNacksBeforeRetransmission),
       use_adaptive_reordering_threshold_(false),
+      use_adaptive_time_threshold_(false),
       least_in_flight_(1),
       packet_number_space_(NUM_PACKET_NUMBER_SPACES) {
   SetLossDetectionType(loss_type);
@@ -190,7 +191,8 @@
     QuicTime ack_receive_time,
     QuicPacketNumber packet_number,
     QuicPacketNumber previous_largest_acked) {
-  if (loss_type_ == kAdaptiveTime && reordering_shift_ > 0) {
+  if ((loss_type_ == kAdaptiveTime || use_adaptive_time_threshold_) &&
+      reordering_shift_ > 0) {
     // Increase reordering fraction such that the packet would not have been
     // declared lost.
     QuicTime::Delta time_needed =
diff --git a/quic/core/congestion_control/general_loss_algorithm.h b/quic/core/congestion_control/general_loss_algorithm.h
index d501cc6..fb85523 100644
--- a/quic/core/congestion_control/general_loss_algorithm.h
+++ b/quic/core/congestion_control/general_loss_algorithm.h
@@ -70,6 +70,12 @@
     use_adaptive_reordering_threshold_ = true;
   }
 
+  bool use_adaptive_time_threshold() const {
+    return use_adaptive_time_threshold_;
+  }
+
+  void enable_adaptive_time_threshold() { use_adaptive_time_threshold_ = true; }
+
  private:
   QuicTime loss_detection_timeout_;
   LossDetectionType loss_type_;
@@ -81,6 +87,8 @@
   QuicPacketCount reordering_threshold_;
   // If true, uses adaptive reordering threshold for loss detection.
   bool use_adaptive_reordering_threshold_;
+  // If true, uses adaptive time threshold for time based loss detection.
+  bool use_adaptive_time_threshold_;
   // The largest newly acked from the previous call to DetectLosses.
   QuicPacketNumber largest_previously_acked_;
   // The least in flight packet. Loss detection should start from this. Please
diff --git a/quic/core/congestion_control/general_loss_algorithm_test.cc b/quic/core/congestion_control/general_loss_algorithm_test.cc
index 0c41f65..aaf9c7c 100644
--- a/quic/core/congestion_control/general_loss_algorithm_test.cc
+++ b/quic/core/congestion_control/general_loss_algorithm_test.cc
@@ -520,6 +520,48 @@
   EXPECT_EQ(1, loss_algorithm_.reordering_shift());
 }
 
+TEST_F(GeneralLossAlgorithmTest, IncreaseTimeThresholdUponSpuriousLoss) {
+  loss_algorithm_.SetLossDetectionType(kIetfLossDetection);
+  loss_algorithm_.enable_adaptive_time_threshold();
+  loss_algorithm_.set_reordering_shift(kDefaultLossDelayShift);
+  EXPECT_EQ(kDefaultLossDelayShift, loss_algorithm_.reordering_shift());
+  EXPECT_TRUE(loss_algorithm_.use_adaptive_time_threshold());
+  const size_t kNumSentPackets = 10;
+  // Transmit 2 packets at 1/10th an RTT interval.
+  for (size_t i = 1; i <= kNumSentPackets; ++i) {
+    SendDataPacket(i);
+    clock_.AdvanceTime(0.1 * rtt_stats_.smoothed_rtt());
+  }
+  EXPECT_EQ(QuicTime::Zero() + rtt_stats_.smoothed_rtt(), clock_.Now());
+  AckedPacketVector packets_acked;
+  // Expect the timer to not be set.
+  EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+  // Packet 1 should not be lost until 1/4 RTTs pass.
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(AckedPacket(
+      QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+  VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
+  packets_acked.clear();
+  // Expect the timer to be set to 1/4 RTT's in the future.
+  EXPECT_EQ(rtt_stats_.smoothed_rtt() * (1.0f / 4),
+            loss_algorithm_.GetLossTimeout() - clock_.Now());
+  VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
+  clock_.AdvanceTime(rtt_stats_.smoothed_rtt() * (1.0f / 4));
+  VerifyLosses(2, packets_acked, {1});
+  EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+  // Retransmit packet 1 as 11 and 2 as 12.
+  SendDataPacket(11);
+  SendDataPacket(12);
+
+  // Advance the time 1/4 RTT and indicate the loss was spurious.
+  // The new threshold should be 1/2 RTT.
+  clock_.AdvanceTime(rtt_stats_.smoothed_rtt() * (1.0f / 4));
+  loss_algorithm_.SpuriousLossDetected(unacked_packets_, rtt_stats_,
+                                       clock_.Now(), QuicPacketNumber(1),
+                                       QuicPacketNumber(2));
+  EXPECT_EQ(1, loss_algorithm_.reordering_shift());
+}
+
 TEST_F(GeneralLossAlgorithmTest, IncreaseReorderingThresholdUponSpuriousLoss) {
   loss_algorithm_.enable_adaptive_reordering_threshold();
   for (size_t i = 1; i <= 4; ++i) {
diff --git a/quic/core/congestion_control/uber_loss_algorithm.cc b/quic/core/congestion_control/uber_loss_algorithm.cc
index 1aa10ff..4e7f07b 100644
--- a/quic/core/congestion_control/uber_loss_algorithm.cc
+++ b/quic/core/congestion_control/uber_loss_algorithm.cc
@@ -93,4 +93,10 @@
   }
 }
 
+void UberLossAlgorithm::EnableAdaptiveTimeThreshold() {
+  for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
+    general_loss_algorithms_[i].enable_adaptive_time_threshold();
+  }
+}
+
 }  // namespace quic
diff --git a/quic/core/congestion_control/uber_loss_algorithm.h b/quic/core/congestion_control/uber_loss_algorithm.h
index a97fe08..f922bba 100644
--- a/quic/core/congestion_control/uber_loss_algorithm.h
+++ b/quic/core/congestion_control/uber_loss_algorithm.h
@@ -54,6 +54,9 @@
   // Enable adaptive reordering threshold of all packet number spaces.
   void EnableAdaptiveReorderingThreshold();
 
+  // Enable adaptive time threshold of all packet number spaces.
+  void EnableAdaptiveTimeThreshold();
+
  private:
   friend class test::QuicSentPacketManagerPeer;
 
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 0aeb1f9..f09c595 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -176,6 +176,11 @@
                                                  // with 1/4 RTT time threshold
                                                  // and adaptive packet
                                                  // threshold
+const QuicTag kILD4 = TAG('I', 'L', 'D', '4');   // IETF style loss detection
+                                                 // with both adaptive time
+                                                 // threshold (default 1/4 RTT)
+                                                 // and adaptive packet
+                                                 // threshold
 // TODO(fayang): Remove this connection option when QUIC_VERSION_35, is removed
 // Since MAX_HEADER_LIST_SIZE settings frame is supported instead.
 const QuicTag kSMHL = TAG('S', 'M', 'H', 'L');   // Support MAX_HEADER_LIST_SIZE
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index 5880ea9..f9ec1c0 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -257,25 +257,32 @@
   }
   if (GetQuicReloadableFlag(quic_enable_ietf_loss_detection)) {
     if (config.HasClientRequestedIndependentOption(kILD0, perspective)) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_ietf_loss_detection, 1, 4);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_ietf_loss_detection, 1, 5);
       uber_loss_algorithm_.SetLossDetectionType(kIetfLossDetection);
     }
     if (config.HasClientRequestedIndependentOption(kILD1, perspective)) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_ietf_loss_detection, 2, 4);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_ietf_loss_detection, 2, 5);
       uber_loss_algorithm_.SetLossDetectionType(kIetfLossDetection);
       uber_loss_algorithm_.SetReorderingShift(kDefaultLossDelayShift);
     }
     if (config.HasClientRequestedIndependentOption(kILD2, perspective)) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_ietf_loss_detection, 3, 4);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_ietf_loss_detection, 3, 5);
       uber_loss_algorithm_.SetLossDetectionType(kIetfLossDetection);
       uber_loss_algorithm_.EnableAdaptiveReorderingThreshold();
     }
     if (config.HasClientRequestedIndependentOption(kILD3, perspective)) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_ietf_loss_detection, 4, 4);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_ietf_loss_detection, 4, 5);
       uber_loss_algorithm_.SetLossDetectionType(kIetfLossDetection);
       uber_loss_algorithm_.SetReorderingShift(kDefaultLossDelayShift);
       uber_loss_algorithm_.EnableAdaptiveReorderingThreshold();
     }
+    if (config.HasClientRequestedIndependentOption(kILD4, perspective)) {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_ietf_loss_detection, 5, 5);
+      uber_loss_algorithm_.SetLossDetectionType(kIetfLossDetection);
+      uber_loss_algorithm_.SetReorderingShift(kDefaultLossDelayShift);
+      uber_loss_algorithm_.EnableAdaptiveReorderingThreshold();
+      uber_loss_algorithm_.EnableAdaptiveTimeThreshold();
+    }
   }
   if (config.HasClientSentConnectionOption(kCONH, perspective)) {
     conservative_handshake_retransmits_ = true;
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index c9d70b9..b7ca395 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -1851,6 +1851,35 @@
       QuicSentPacketManagerPeer::AdaptiveReorderingThresholdEnabled(&manager_));
 }
 
+TEST_F(QuicSentPacketManagerTest,
+       NegotiateIetfLossDetectionAdaptiveReorderingAndTimeThreshold) {
+  SetQuicReloadableFlag(quic_enable_ietf_loss_detection, true);
+  EXPECT_EQ(kNack, QuicSentPacketManagerPeer::GetLossAlgorithm(&manager_)
+                       ->GetLossDetectionType());
+  EXPECT_FALSE(
+      QuicSentPacketManagerPeer::AdaptiveReorderingThresholdEnabled(&manager_));
+  EXPECT_FALSE(
+      QuicSentPacketManagerPeer::AdaptiveTimeThresholdEnabled(&manager_));
+
+  QuicConfig config;
+  QuicTagVector options;
+  options.push_back(kILD4);
+  QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  manager_.SetFromConfig(config);
+
+  EXPECT_EQ(kIetfLossDetection,
+            QuicSentPacketManagerPeer::GetLossAlgorithm(&manager_)
+                ->GetLossDetectionType());
+  EXPECT_EQ(kDefaultLossDelayShift,
+            QuicSentPacketManagerPeer::GetReorderingShift(&manager_));
+  EXPECT_TRUE(
+      QuicSentPacketManagerPeer::AdaptiveReorderingThresholdEnabled(&manager_));
+  EXPECT_TRUE(
+      QuicSentPacketManagerPeer::AdaptiveTimeThresholdEnabled(&manager_));
+}
+
 TEST_F(QuicSentPacketManagerTest, NegotiateCongestionControlFromOptions) {
   QuicConfig config;
   QuicTagVector options;
diff --git a/quic/test_tools/quic_sent_packet_manager_peer.cc b/quic/test_tools/quic_sent_packet_manager_peer.cc
index 7b3977e..cd8297a 100644
--- a/quic/test_tools/quic_sent_packet_manager_peer.cc
+++ b/quic/test_tools/quic_sent_packet_manager_peer.cc
@@ -214,5 +214,12 @@
       .use_adaptive_reordering_threshold();
 }
 
+// static
+bool QuicSentPacketManagerPeer::AdaptiveTimeThresholdEnabled(
+    QuicSentPacketManager* sent_packet_manager) {
+  return sent_packet_manager->uber_loss_algorithm_.general_loss_algorithms_[0]
+      .use_adaptive_time_threshold();
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/quic_sent_packet_manager_peer.h b/quic/test_tools/quic_sent_packet_manager_peer.h
index 6be8b46..3927189 100644
--- a/quic/test_tools/quic_sent_packet_manager_peer.h
+++ b/quic/test_tools/quic_sent_packet_manager_peer.h
@@ -98,6 +98,9 @@
 
   static bool AdaptiveReorderingThresholdEnabled(
       QuicSentPacketManager* sent_packet_manager);
+
+  static bool AdaptiveTimeThresholdEnabled(
+      QuicSentPacketManager* sent_packet_manager);
 };
 
 }  // namespace test