gfe-relnote: In QUIC, enable round robin scheduling. Protected by gfe2_reloadable_flag_quic_enable_rr_write_schduler.

PiperOrigin-RevId: 269414968
Change-Id: Id2e97b3eea61cbd0f8c140f970715f21af90c156
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 8adf0ae..5f98345 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -226,6 +226,7 @@
                                                 // has the highest priority.
 const QuicTag kLIFO = TAG('L', 'I', 'F', 'O');  // Stream with the largest ID
                                                 // has the highest priority.
+const QuicTag kRRWS = TAG('R', 'R', 'W', 'S');  // Round robin write scheduling.
 
 // Proof types (i.e. certificate types)
 // NOTE: although it would be silly to do so, specifying both kX509 and kX59R
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 9b812de..9ede3f8 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -93,7 +93,8 @@
       use_http2_priority_write_scheduler_(false),
       is_configured_(false),
       num_expected_unidirectional_static_streams_(
-          num_expected_unidirectional_static_streams) {
+          num_expected_unidirectional_static_streams),
+      enable_round_robin_scheduling_(false) {
   closed_streams_clean_up_alarm_ =
       QuicWrapUnique<QuicAlarm>(connection_->alarm_factory()->CreateAlarm(
           new ClosedStreamsCleanUpDelegate(this)));
@@ -1000,6 +1001,12 @@
                 spdy::WriteSchedulerType::LIFO, transport_version())) {
           QUIC_RELOADABLE_FLAG_COUNT(quic_enable_lifo_write_scheduler);
         }
+      } else if (GetQuicReloadableFlag(quic_enable_rr_write_scheduler) &&
+                 ContainsQuicTag(config_.ReceivedConnectionOptions(), kRRWS) &&
+                 write_blocked_streams_.scheduler_type() ==
+                     spdy::WriteSchedulerType::SPDY) {
+        QUIC_RELOADABLE_FLAG_COUNT(quic_enable_rr_write_scheduler);
+        enable_round_robin_scheduling_ = true;
       }
     }
 
@@ -1157,6 +1164,13 @@
     QuicStreamId id,
     bool is_static,
     const spdy::SpdyStreamPrecedence& precedence) {
+  if (enable_round_robin_scheduling_) {
+    // Ignore provided precedence, instead, put all streams at the same priority
+    // bucket.
+    write_blocked_streams()->RegisterStream(
+        id, is_static, spdy::SpdyStreamPrecedence(spdy::kV3LowestPriority));
+    return;
+  }
   write_blocked_streams()->RegisterStream(id, is_static, precedence);
 }
 
@@ -1167,6 +1181,10 @@
 void QuicSession::UpdateStreamPriority(
     QuicStreamId id,
     const spdy::SpdyStreamPrecedence& new_precedence) {
+  if (enable_round_robin_scheduling_) {
+    // Ignore updated precedence.
+    return;
+  }
   write_blocked_streams()->UpdateStreamPriority(id, new_precedence);
 }
 
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 5250f9b..6fb4c8d 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -756,6 +756,9 @@
 
   // The number of expected static streams.
   QuicStreamCount num_expected_unidirectional_static_streams_;
+
+  // If true, enables round robin scheduling.
+  bool enable_round_robin_scheduling_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index f7ab416..004af59 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -1000,6 +1000,50 @@
   session_.OnCanWrite();
 }
 
+TEST_P(QuicSessionTestServer, RoundRobinScheduling) {
+  if (VersionHasIetfQuicFrames(GetParam().transport_version)) {
+    return;
+  }
+  SetQuicReloadableFlag(quic_enable_rr_write_scheduler, true);
+  QuicTagVector copt;
+  copt.push_back(kRRWS);
+  QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt);
+  session_.OnConfigNegotiated();
+
+  session_.set_writev_consumes_all_data(true);
+  TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+  TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+  TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+  session_.set_writev_consumes_all_data(true);
+  session_.MarkConnectionLevelWriteBlocked(stream2->id());
+  session_.MarkConnectionLevelWriteBlocked(stream4->id());
+  session_.MarkConnectionLevelWriteBlocked(stream6->id());
+
+  // Verify streams are scheduled round robin.
+  InSequence s;
+  EXPECT_CALL(*stream2, OnCanWrite());
+  EXPECT_CALL(*stream4, OnCanWrite());
+  EXPECT_CALL(*stream6, OnCanWrite());
+  session_.OnCanWrite();
+
+  /* 2, 4, 6, 8 */
+  TestStream* stream8 = session_.CreateOutgoingBidirectionalStream();
+
+  // Verify updated priority is ignored.
+  stream4->SetPriority(spdy::SpdyStreamPrecedence(spdy::kV3HighestPriority));
+  session_.MarkConnectionLevelWriteBlocked(stream8->id());
+  session_.MarkConnectionLevelWriteBlocked(stream4->id());
+  session_.MarkConnectionLevelWriteBlocked(stream2->id());
+  session_.MarkConnectionLevelWriteBlocked(stream6->id());
+
+  EXPECT_CALL(*stream8, OnCanWrite());
+  EXPECT_CALL(*stream4, OnCanWrite());
+  EXPECT_CALL(*stream2, OnCanWrite());
+  EXPECT_CALL(*stream6, OnCanWrite());
+  session_.OnCanWrite();
+}
+
 TEST_P(QuicSessionTestServer, OnCanWriteBundlesStreams) {
   // Encryption needs to be established before data can be sent.
   CryptoHandshakeMessage msg;
diff --git a/quic/core/quic_write_blocked_list.h b/quic/core/quic_write_blocked_list.h
index 72ebc05..f77a2cd 100644
--- a/quic/core/quic_write_blocked_list.h
+++ b/quic/core/quic_write_blocked_list.h
@@ -210,6 +210,8 @@
     return priority_write_scheduler_->IsStreamReady(stream_id);
   }
 
+  spdy::WriteSchedulerType scheduler_type() const { return scheduler_type_; }
+
  private:
   bool PrecedenceMatchesSchedulerType(
       const spdy::SpdyStreamPrecedence& precedence) {