gfe-relnote: Move LifoWriteScheduler from gfe/gfe2/spdy/ to third_party/spdy/core to allow QUIC to use it. No functional change expected, no flag protected.

PiperOrigin-RevId: 261692309
Change-Id: I2960c067af53ed2de07a69f9f927018193c7c857
diff --git a/spdy/core/lifo_write_scheduler.h b/spdy/core/lifo_write_scheduler.h
new file mode 100644
index 0000000..ed734f3
--- /dev/null
+++ b/spdy/core/lifo_write_scheduler.h
@@ -0,0 +1,207 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_
+#define QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_
+
+#include <map>
+#include <set>
+
+#include "net/third_party/quiche/src/spdy/core/write_scheduler.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_containers.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h"
+
+namespace spdy {
+
+namespace test {
+
+template <typename StreamIdType>
+class LifoWriteSchedulerPeer;
+
+}  // namespace test
+
+// Create a write scheduler where the stream added last will have the highest
+// priority.
+template <typename StreamIdType>
+class LifoWriteScheduler : public WriteScheduler<StreamIdType> {
+ public:
+  using typename WriteScheduler<StreamIdType>::StreamPrecedenceType;
+
+  LifoWriteScheduler() = default;
+
+  void RegisterStream(StreamIdType stream_id,
+                      const StreamPrecedenceType& /*precedence*/) override;
+
+  void UnregisterStream(StreamIdType stream_id) override;
+
+  bool StreamRegistered(StreamIdType stream_id) const override {
+    return registered_streams_.find(stream_id) != registered_streams_.end();
+  }
+
+  // Stream precedence is not supported by this scheduler.
+  StreamPrecedenceType GetStreamPrecedence(
+      StreamIdType /*stream_id*/) const override {
+    return StreamPrecedenceType(kV3LowestPriority);
+  }
+
+  void UpdateStreamPrecedence(
+      StreamIdType /*stream_id*/,
+      const StreamPrecedenceType& /*precedence*/) override {}
+
+  std::vector<StreamIdType> GetStreamChildren(
+      StreamIdType /*stream_id*/) const override {
+    return std::vector<StreamIdType>();
+  }
+
+  void RecordStreamEventTime(StreamIdType stream_id,
+                             int64_t now_in_usec) override;
+
+  int64_t GetLatestEventWithPrecedence(StreamIdType stream_id) const override;
+
+  StreamIdType PopNextReadyStream() override;
+
+  std::tuple<StreamIdType, StreamPrecedenceType>
+  PopNextReadyStreamAndPrecedence() override {
+    return std::make_tuple(PopNextReadyStream(),
+                           StreamPrecedenceType(kV3LowestPriority));
+  }
+
+  bool ShouldYield(StreamIdType stream_id) const override {
+    return !ready_streams_.empty() && stream_id < *ready_streams_.rbegin();
+  }
+
+  void MarkStreamReady(StreamIdType stream_id, bool /*add_to_front*/) override;
+
+  void MarkStreamNotReady(StreamIdType stream_id) override;
+
+  bool HasReadyStreams() const override { return !ready_streams_.empty(); }
+  size_t NumReadyStreams() const override { return ready_streams_.size(); }
+  bool IsStreamReady(StreamIdType stream_id) const override;
+  size_t NumRegisteredStreams() const override;
+  string DebugString() const override;
+
+ private:
+  friend class test::LifoWriteSchedulerPeer<StreamIdType>;
+
+  std::set<StreamIdType> ready_streams_;
+  std::map<StreamIdType, int64_t> registered_streams_;
+};
+
+template <typename StreamIdType>
+void LifoWriteScheduler<StreamIdType>::RegisterStream(
+    StreamIdType stream_id,
+    const StreamPrecedenceType& /*precedence*/) {
+  if (StreamRegistered(stream_id)) {
+    SPDY_BUG << "Stream " << stream_id << " already registered";
+    return;
+  }
+  registered_streams_.emplace_hint(registered_streams_.end(), stream_id, 0);
+}
+
+template <typename StreamIdType>
+void LifoWriteScheduler<StreamIdType>::UnregisterStream(
+    StreamIdType stream_id) {
+  if (!StreamRegistered(stream_id)) {
+    SPDY_BUG << "Stream " << stream_id << " is not registered";
+    return;
+  }
+  registered_streams_.erase(stream_id);
+  ready_streams_.erase(stream_id);
+}
+
+template <typename StreamIdType>
+void LifoWriteScheduler<StreamIdType>::RecordStreamEventTime(
+    StreamIdType stream_id,
+    int64_t now_in_usec) {
+  auto it = registered_streams_.find(stream_id);
+  if (it != registered_streams_.end()) {
+    it->second = now_in_usec;
+  } else {
+    SPDY_BUG << "Stream " << stream_id << " is not registered";
+  }
+}
+
+template <typename StreamIdType>
+int64_t LifoWriteScheduler<StreamIdType>::GetLatestEventWithPrecedence(
+    StreamIdType stream_id) const {
+  if (!StreamRegistered(stream_id)) {
+    SPDY_BUG << "Stream " << stream_id << " is not registered";
+    return 0;
+  }
+  int64_t latest_event_time_us = 0;
+  for (auto it = registered_streams_.rbegin(); it != registered_streams_.rend();
+       ++it) {
+    if (stream_id < it->first) {
+      if (it->second > latest_event_time_us) {
+        latest_event_time_us = it->second;
+      }
+    } else {
+      break;
+    }
+  }
+  return latest_event_time_us;
+}
+
+template <typename StreamIdType>
+StreamIdType LifoWriteScheduler<StreamIdType>::PopNextReadyStream() {
+  if (ready_streams_.empty()) {
+    SPDY_BUG << "No ready streams available";
+    return 0;
+  }
+  auto it = --ready_streams_.end();
+  StreamIdType id = *it;
+  ready_streams_.erase(it);
+  return id;
+}
+
+template <typename StreamIdType>
+void LifoWriteScheduler<StreamIdType>::MarkStreamReady(StreamIdType stream_id,
+                                                       bool /*add_to_front*/) {
+  if (!StreamRegistered(stream_id)) {
+    SPDY_BUG << "Stream " << stream_id << " is not registered";
+    return;
+  }
+  if (ready_streams_.find(stream_id) != ready_streams_.end()) {
+    SPDY_VLOG(1) << "Stream already exists in the list";
+    return;
+  }
+  ready_streams_.insert(stream_id);
+}
+
+template <typename StreamIdType>
+void LifoWriteScheduler<StreamIdType>::MarkStreamNotReady(
+    StreamIdType stream_id) {
+  auto it = ready_streams_.find(stream_id);
+  if (it == ready_streams_.end()) {
+    SPDY_VLOG(1) << "Try to remove a stream that is not on list";
+    return;
+  }
+  ready_streams_.erase(it);
+}
+
+template <typename StreamIdType>
+bool LifoWriteScheduler<StreamIdType>::IsStreamReady(
+    StreamIdType stream_id) const {
+  if (!StreamRegistered(stream_id)) {
+    SPDY_BUG << "Stream " << stream_id << " is not registered";
+    return false;
+  }
+  return ready_streams_.find(stream_id) != ready_streams_.end();
+}
+
+template <typename StreamIdType>
+size_t LifoWriteScheduler<StreamIdType>::NumRegisteredStreams() const {
+  return registered_streams_.size();
+}
+
+template <typename StreamIdType>
+SpdyString LifoWriteScheduler<StreamIdType>::DebugString() const {
+  return SpdyStrCat(
+      "LifoWriteScheduler {num_streams=", registered_streams_.size(),
+      " num_ready_streams=", NumReadyStreams(), "}");
+}
+
+}  // namespace spdy
+
+#endif  // QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_
diff --git a/spdy/core/lifo_write_scheduler_test.cc b/spdy/core/lifo_write_scheduler_test.cc
new file mode 100644
index 0000000..c9acc11
--- /dev/null
+++ b/spdy/core/lifo_write_scheduler_test.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h"
+
+#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_test_helpers.h"
+
+namespace spdy {
+
+namespace test {
+
+template <typename StreamIdType>
+class LifoWriteSchedulerPeer {
+ public:
+  explicit LifoWriteSchedulerPeer(LifoWriteScheduler<StreamIdType>* scheduler)
+      : scheduler_(scheduler) {}
+
+  size_t NumRegisteredListStreams() const {
+    return scheduler_->registered_streams_.size();
+  }
+
+  std::set<StreamIdType>* GetReadyList() const {
+    return &scheduler_->ready_streams_;
+  }
+
+ private:
+  LifoWriteScheduler<StreamIdType>* scheduler_;
+};
+
+// Test add and remove from ready list.
+TEST(LifoWriteSchedulerTest, ReadyListTest) {
+  LifoWriteScheduler<SpdyStreamId> lifo;
+  LifoWriteSchedulerPeer<SpdyStreamId> peer(&lifo);
+
+  EXPECT_SPDY_BUG(
+      EXPECT_EQ(0u, std::get<0>(lifo.PopNextReadyStreamAndPrecedence())),
+      "No ready streams available");
+  EXPECT_SPDY_BUG(EXPECT_EQ(0u, lifo.PopNextReadyStream()),
+                  "No ready streams available");
+  EXPECT_FALSE(lifo.HasReadyStreams());
+  EXPECT_SPDY_BUG(lifo.MarkStreamReady(9, true), "Stream 9 is not registered");
+  EXPECT_SPDY_BUG(lifo.IsStreamReady(9), "Stream 9 is not registered");
+  SpdyStreamPrecedence precedence(1);
+  lifo.RegisterStream(3, precedence);
+  EXPECT_FALSE(lifo.IsStreamReady(3));
+  lifo.RegisterStream(7, precedence);
+  lifo.RegisterStream(9, precedence);
+  lifo.RegisterStream(11, precedence);
+  lifo.RegisterStream(13, precedence);
+  lifo.RegisterStream(15, precedence);
+  lifo.RegisterStream(17, precedence);
+  lifo.MarkStreamReady(9, true);
+  lifo.MarkStreamReady(15, true);
+  lifo.MarkStreamReady(7, true);
+  lifo.MarkStreamReady(13, true);
+  lifo.MarkStreamReady(11, true);
+  lifo.MarkStreamReady(3, true);
+  EXPECT_TRUE(lifo.IsStreamReady(3));
+  lifo.MarkStreamReady(17, true);
+  EXPECT_TRUE(lifo.HasReadyStreams());
+  EXPECT_EQ(7u, lifo.NumReadyStreams());
+
+  // Verify MarkStream(Not)Ready() can be called multiple times for the same
+  // stream.
+  lifo.MarkStreamReady(11, true);
+  lifo.MarkStreamNotReady(5);
+  lifo.MarkStreamNotReady(21);
+
+  EXPECT_EQ(17u, lifo.PopNextReadyStream());
+  EXPECT_EQ(15u, std::get<0>(lifo.PopNextReadyStreamAndPrecedence()));
+  EXPECT_TRUE(lifo.ShouldYield(9));
+  EXPECT_FALSE(lifo.ShouldYield(13));
+  EXPECT_FALSE(lifo.ShouldYield(15));
+
+  lifo.MarkStreamNotReady(3);
+  EXPECT_TRUE(peer.GetReadyList()->find(3) == peer.GetReadyList()->end());
+  lifo.MarkStreamNotReady(13);
+  EXPECT_TRUE(peer.GetReadyList()->find(13) == peer.GetReadyList()->end());
+  lifo.MarkStreamNotReady(7);
+  EXPECT_TRUE(peer.GetReadyList()->find(7) == peer.GetReadyList()->end());
+  EXPECT_EQ(2u, lifo.NumReadyStreams());
+
+  lifo.MarkStreamNotReady(9);
+  lifo.MarkStreamNotReady(11);
+  EXPECT_FALSE(lifo.ShouldYield(1));
+}
+
+// Test add and remove from registered list.
+TEST(LifoWriteSchedulerTest, RegisterListTest) {
+  LifoWriteScheduler<SpdyStreamId> lifo;
+  LifoWriteSchedulerPeer<SpdyStreamId> peer(&lifo);
+  SpdyStreamPrecedence precedence(1);
+  EXPECT_EQ(0u, lifo.NumRegisteredStreams());
+  lifo.RegisterStream(3, precedence);
+  lifo.RegisterStream(5, precedence);
+  lifo.RegisterStream(7, precedence);
+  lifo.RegisterStream(9, precedence);
+  lifo.RegisterStream(11, precedence);
+  EXPECT_EQ(5u, lifo.NumRegisteredStreams());
+
+  EXPECT_TRUE(lifo.StreamRegistered(3));
+  EXPECT_TRUE(lifo.StreamRegistered(5));
+  EXPECT_TRUE(lifo.StreamRegistered(7));
+  EXPECT_TRUE(lifo.StreamRegistered(9));
+  EXPECT_TRUE(lifo.StreamRegistered(11));
+  EXPECT_SPDY_BUG(lifo.RegisterStream(11, precedence),
+                  "Stream 11 already registered");
+  EXPECT_EQ(5u, peer.NumRegisteredListStreams());
+
+  lifo.UnregisterStream(3);
+  EXPECT_EQ(4u, lifo.NumRegisteredStreams());
+  EXPECT_FALSE(lifo.StreamRegistered(3));
+  EXPECT_SPDY_BUG(lifo.UnregisterStream(3), "Stream 3 is not registered");
+  EXPECT_SPDY_BUG(lifo.UnregisterStream(13), "Stream 13 is not registered");
+  lifo.UnregisterStream(11);
+  EXPECT_FALSE(lifo.StreamRegistered(11));
+  lifo.UnregisterStream(7);
+  EXPECT_EQ(2u, lifo.NumRegisteredStreams());
+  EXPECT_FALSE(lifo.StreamRegistered(7));
+  EXPECT_TRUE(lifo.StreamRegistered(5));
+  EXPECT_TRUE(lifo.StreamRegistered(9));
+}
+
+// Test mark latest event time.
+TEST(LifoWriteSchedulerTest, GetLatestEventTest) {
+  LifoWriteScheduler<SpdyStreamId> lifo;
+  LifoWriteSchedulerPeer<SpdyStreamId> peer(&lifo);
+  SpdyStreamPrecedence precedence(1);
+  lifo.RegisterStream(1, precedence);
+  lifo.RegisterStream(3, precedence);
+  lifo.RegisterStream(5, precedence);
+  lifo.RegisterStream(7, precedence);
+  lifo.RegisterStream(9, precedence);
+  lifo.RecordStreamEventTime(1, 1);
+  lifo.RecordStreamEventTime(3, 8);
+  lifo.RecordStreamEventTime(5, 4);
+  lifo.RecordStreamEventTime(7, 2);
+  lifo.RecordStreamEventTime(9, 3);
+  EXPECT_SPDY_BUG(lifo.RecordStreamEventTime(11, 1),
+                  "Stream 11 is not registered");
+  EXPECT_EQ(0, lifo.GetLatestEventWithPrecedence(9));
+  EXPECT_EQ(3, lifo.GetLatestEventWithPrecedence(7));
+  EXPECT_EQ(3, lifo.GetLatestEventWithPrecedence(5));
+  EXPECT_EQ(4, lifo.GetLatestEventWithPrecedence(3));
+  EXPECT_EQ(8, lifo.GetLatestEventWithPrecedence(1));
+  EXPECT_SPDY_BUG(lifo.GetLatestEventWithPrecedence(11),
+                  "Stream 11 is not registered");
+}
+
+}  // namespace test
+
+}  // namespace spdy