gfe-relnote: Let GFE be able to use FIFO write scheduler in QUIC and enable it via a connection option FIFO. Protected by gfe2_reloadable_flag_quic_use_fifo_write_scheduler.

PiperOrigin-RevId: 261676376
Change-Id: I74a4d92ad6d612c565eaa1e38186738988d02fe5
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 81f9730..0facf00 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -199,6 +199,8 @@
 
 // Enable Priority scheme experiment.
 const QuicTag kH2PR = TAG('H', '2', 'P', 'R');  // HTTP2 priorities.
+const QuicTag kFIFO = TAG('F', 'I', 'F', 'O');  // Stream with the smallest ID
+                                                // has the highest priority.
 
 // 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/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index c61c347..2e79f78 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -85,11 +85,13 @@
   TestParams(const ParsedQuicVersionVector& client_supported_versions,
              const ParsedQuicVersionVector& server_supported_versions,
              ParsedQuicVersion negotiated_version,
-             QuicTag congestion_control_tag)
+             QuicTag congestion_control_tag,
+             QuicTag priority_tag)
       : client_supported_versions(client_supported_versions),
         server_supported_versions(server_supported_versions),
         negotiated_version(negotiated_version),
-        congestion_control_tag(congestion_control_tag) {}
+        congestion_control_tag(congestion_control_tag),
+        priority_tag(priority_tag) {}
 
   friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
     os << "{ server_supported_versions: "
@@ -99,7 +101,8 @@
     os << " negotiated_version: "
        << ParsedQuicVersionToString(p.negotiated_version);
     os << " congestion_control_tag: "
-       << QuicTagToString(p.congestion_control_tag) << " }";
+       << QuicTagToString(p.congestion_control_tag);
+    os << " priority_tag: " << QuicTagToString(p.priority_tag) << " }";
     return os;
   }
 
@@ -107,6 +110,7 @@
   ParsedQuicVersionVector server_supported_versions;
   ParsedQuicVersion negotiated_version;
   QuicTag congestion_control_tag;
+  QuicTag priority_tag;
 };
 
 // Constructs various test permutations.
@@ -153,28 +157,32 @@
       if (FilterSupportedVersions(client_versions).empty()) {
         continue;
       }
-      // Add an entry for server and client supporting all versions.
-      params.push_back(TestParams(client_versions, all_supported_versions,
-                                  client_versions.front(),
-                                  congestion_control_tag));
+      for (const QuicTag priority_tag :
+           {/*no tag*/ static_cast<QuicTag>(0), kH2PR, kFIFO}) {
+        // Add an entry for server and client supporting all versions.
+        params.push_back(TestParams(client_versions, all_supported_versions,
+                                    client_versions.front(),
+                                    congestion_control_tag, priority_tag));
 
-      // Test client supporting all versions and server supporting
-      // 1 version. Simulate an old server and exercise version
-      // downgrade in the client. Protocol negotiation should
-      // occur.  Skip the i = 0 case because it is essentially the
-      // same as the default case.
-      for (size_t i = 1; i < client_versions.size(); ++i) {
-        ParsedQuicVersionVector server_supported_versions;
-        server_supported_versions.push_back(client_versions[i]);
-        if (FilterSupportedVersions(server_supported_versions).empty()) {
-          continue;
-        }
-        params.push_back(TestParams(client_versions, server_supported_versions,
-                                    server_supported_versions.front(),
-                                    congestion_control_tag));
-      }  // End of inner version loop.
-    }    // End of outer version loop.
-  }      // End of congestion_control_tag loop.
+        // Test client supporting all versions and server supporting
+        // 1 version. Simulate an old server and exercise version
+        // downgrade in the client. Protocol negotiation should
+        // occur.  Skip the i = 0 case because it is essentially the
+        // same as the default case.
+        for (size_t i = 1; i < client_versions.size(); ++i) {
+          ParsedQuicVersionVector server_supported_versions;
+          server_supported_versions.push_back(client_versions[i]);
+          if (FilterSupportedVersions(server_supported_versions).empty()) {
+            continue;
+          }
+          params.push_back(TestParams(client_versions,
+                                      server_supported_versions,
+                                      server_supported_versions.front(),
+                                      congestion_control_tag, priority_tag));
+        }  // End of inner version loop.
+      }    // End of priority_tag loop.
+    }      // End of outer version loop.
+  }        // End of congestion_control_tag loop.
 
   return params;
 }
@@ -343,9 +351,7 @@
         GetQuicReloadableFlag(quic_enable_pcc3)) {
       copt.push_back(kTPCC);
     }
-    // TODO(fayang): Move this to GetTestParams when other priority connection
-    // opts are supported.
-    copt.push_back(kH2PR);
+    copt.push_back(GetParam().priority_tag);
 
     client_config_.SetConnectionOptionsToSend(copt);
 
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index ba53d5e..e39fd41 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -998,12 +998,22 @@
       if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFWA)) {
         AdjustInitialFlowControlWindows(1024 * 1024);
       }
-      // Enable HTTP2 (tree-style) priority write scheduler.
       if (GetQuicReloadableFlag(quic_use_http2_priority_write_scheduler) &&
           ContainsQuicTag(config_.ReceivedConnectionOptions(), kH2PR) &&
           !VersionHasIetfQuicFrames(connection_->transport_version())) {
+        // Enable HTTP2 (tree-style) priority write scheduler.
         use_http2_priority_write_scheduler_ =
-            write_blocked_streams_.UseHttp2PriorityScheduler();
+            write_blocked_streams_.SwitchWriteScheduler(
+                spdy::WriteSchedulerType::HTTP2,
+                connection_->transport_version());
+      } else if (GetQuicReloadableFlag(quic_enable_fifo_write_scheduler) &&
+                 ContainsQuicTag(config_.ReceivedConnectionOptions(), kFIFO)) {
+        // Enable FIFO write scheduler.
+        if (write_blocked_streams_.SwitchWriteScheduler(
+                spdy::WriteSchedulerType::FIFO,
+                connection_->transport_version())) {
+          QUIC_RELOADABLE_FLAG_COUNT(quic_enable_fifo_write_scheduler);
+        }
       }
     }
 
diff --git a/quic/core/quic_write_blocked_list.cc b/quic/core/quic_write_blocked_list.cc
index c9b9c22..8e4bcf1 100644
--- a/quic/core/quic_write_blocked_list.cc
+++ b/quic/core/quic_write_blocked_list.cc
@@ -6,7 +6,6 @@
 
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
-#include "net/third_party/quiche/src/spdy/core/priority_write_scheduler.h"
 
 namespace quic {
 
diff --git a/quic/core/quic_write_blocked_list.h b/quic/core/quic_write_blocked_list.h
index 17bc695..dd9ddb4 100644
--- a/quic/core/quic_write_blocked_list.h
+++ b/quic/core/quic_write_blocked_list.h
@@ -14,7 +14,9 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h"
 #include "net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler.h"
+#include "net/third_party/quiche/src/spdy/core/priority_write_scheduler.h"
 
 namespace quic {
 
@@ -63,20 +65,41 @@
     return priority_write_scheduler_->ShouldYield(id);
   }
 
-  // Uses HTTP2 (tree-style) priority scheduler. This can only be called before
-  // any stream is registered.
-  bool UseHttp2PriorityScheduler() {
-    if (scheduler_type_ == spdy::WriteSchedulerType::HTTP2) {
+  // Switches write scheduler. This can only be called before any stream is
+  // registered.
+  bool SwitchWriteScheduler(spdy::WriteSchedulerType type,
+                            QuicTransportVersion version) {
+    if (scheduler_type_ == type) {
       return true;
     }
     if (priority_write_scheduler_->NumRegisteredStreams() != 0) {
       QUIC_BUG << "Cannot switch scheduler with registered streams";
       return false;
     }
-    QUIC_DVLOG(1) << "Using HTTP2 Priority Scheduler";
-    priority_write_scheduler_ =
-        QuicMakeUnique<spdy::Http2PriorityWriteScheduler<QuicStreamId>>();
-    scheduler_type_ = spdy::WriteSchedulerType::HTTP2;
+    QUIC_DVLOG(1) << "Switching to scheduler type: "
+                  << spdy::WriteSchedulerTypeToString(type);
+    switch (type) {
+      case spdy::WriteSchedulerType::SPDY:
+        priority_write_scheduler_ =
+            QuicMakeUnique<spdy::PriorityWriteScheduler<QuicStreamId>>(
+                QuicVersionUsesCryptoFrames(version)
+                    ? std::numeric_limits<QuicStreamId>::max()
+                    : 0);
+        break;
+      case spdy::WriteSchedulerType::HTTP2:
+        priority_write_scheduler_ =
+            QuicMakeUnique<spdy::Http2PriorityWriteScheduler<QuicStreamId>>();
+        break;
+      case spdy::WriteSchedulerType::FIFO:
+        priority_write_scheduler_ =
+            QuicMakeUnique<spdy::FifoWriteScheduler<QuicStreamId>>();
+        break;
+      default:
+        QUIC_BUG << "Scheduler is not supported for type: "
+                 << spdy::WriteSchedulerTypeToString(type);
+        return false;
+    }
+    scheduler_type_ = type;
     return true;
   }
 
@@ -92,8 +115,8 @@
     const auto id_and_precedence =
         priority_write_scheduler_->PopNextReadyStreamAndPrecedence();
     const QuicStreamId id = std::get<0>(id_and_precedence);
-    if (scheduler_type_ == spdy::WriteSchedulerType::HTTP2) {
-      // No batch writing logic when using HTTP2 priorities.
+    if (scheduler_type_ != spdy::WriteSchedulerType::SPDY) {
+      // No batch writing logic for non-SPDY priority write scheduler.
       return id;
     }
     const spdy::SpdyPriority priority =
@@ -144,7 +167,7 @@
   }
 
   void UpdateBytesForStream(QuicStreamId stream_id, size_t bytes) {
-    if (scheduler_type_ == spdy::WriteSchedulerType::HTTP2) {
+    if (scheduler_type_ != spdy::WriteSchedulerType::SPDY) {
       return;
     }
     if (batch_write_stream_id_[last_priority_popped_] == stream_id) {
@@ -185,10 +208,18 @@
  private:
   bool PrecedenceMatchesSchedulerType(
       const spdy::SpdyStreamPrecedence& precedence) {
-    if (precedence.is_spdy3_priority()) {
-      return scheduler_type_ == spdy::WriteSchedulerType::SPDY;
+    switch (scheduler_type_) {
+      case spdy::WriteSchedulerType::SPDY:
+        return precedence.is_spdy3_priority();
+      case spdy::WriteSchedulerType::HTTP2:
+        return !precedence.is_spdy3_priority();
+      case spdy::WriteSchedulerType::FIFO:
+        break;
+      default:
+        DCHECK(false);
+        return false;
     }
-    return scheduler_type_ == spdy::WriteSchedulerType::HTTP2;
+    return true;
   }
 
   std::unique_ptr<QuicPriorityWriteScheduler> priority_write_scheduler_;
diff --git a/quic/core/quic_write_blocked_list_test.cc b/quic/core/quic_write_blocked_list_test.cc
index ca3433b..5d4b639 100644
--- a/quic/core/quic_write_blocked_list_test.cc
+++ b/quic/core/quic_write_blocked_list_test.cc
@@ -22,7 +22,9 @@
   QuicWriteBlockedListTest()
       : write_blocked_list_(AllSupportedVersions()[0].transport_version) {
     if (GetParam()) {
-      write_blocked_list_.UseHttp2PriorityScheduler();
+      write_blocked_list_.SwitchWriteScheduler(
+          spdy::WriteSchedulerType::HTTP2,
+          AllSupportedVersions()[0].transport_version);
     }
   }
 
diff --git a/spdy/core/spdy_protocol.cc b/spdy/core/spdy_protocol.cc
index d9b642f..9094b47 100644
--- a/spdy/core/spdy_protocol.cc
+++ b/spdy/core/spdy_protocol.cc
@@ -229,6 +229,20 @@
   return "UNKNOWN_ERROR_CODE";
 }
 
+const char* WriteSchedulerTypeToString(WriteSchedulerType type) {
+  switch (type) {
+    case WriteSchedulerType::LIFO:
+      return "LIFO";
+    case WriteSchedulerType::SPDY:
+      return "SPDY";
+    case WriteSchedulerType::HTTP2:
+      return "HTTP2";
+    case WriteSchedulerType::FIFO:
+      return "FIFO";
+  }
+  return "UNKNOWN";
+}
+
 size_t GetNumberRequiredContinuationFrames(size_t size) {
   DCHECK_GT(size, kHttp2MaxControlFrameSendSize);
   size_t overflow = size - kHttp2MaxControlFrameSendSize;
diff --git a/spdy/core/spdy_protocol.h b/spdy/core/spdy_protocol.h
index 2d2ce96..2a521ab 100644
--- a/spdy/core/spdy_protocol.h
+++ b/spdy/core/spdy_protocol.h
@@ -203,6 +203,7 @@
          // https://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1#TOC-2.3.3-Stream-priority.
   HTTP2,  // Uses HTTP2 (tree-style) priority described in
           // https://tools.ietf.org/html/rfc7540#section-5.3.
+  FIFO,   // Stream with the smallest stream ID has the highest priority.
 };
 
 // A SPDY priority is a number between 0 and 7 (inclusive).
@@ -284,6 +285,9 @@
 // for logging/debugging.
 const char* ErrorCodeToString(SpdyErrorCode error_code);
 
+// Serialize |type| to string for logging/debugging.
+const char* WriteSchedulerTypeToString(WriteSchedulerType type);
+
 // Minimum size of a frame, in octets.
 const size_t kFrameMinimumSize = kFrameHeaderSize;