gfe-relnote: Add a FifoWriteScheduler where the stream with the smallest stream ID will have the highest priority. Not used in prod, not protected. PiperOrigin-RevId: 260922889 Change-Id: Id7af765ee6e69821b560b0f4c028ac9e8f2840db
diff --git a/spdy/core/fifo_write_scheduler.h b/spdy/core/fifo_write_scheduler.h new file mode 100644 index 0000000..e972a2b --- /dev/null +++ b/spdy/core/fifo_write_scheduler.h
@@ -0,0 +1,219 @@ +// 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_FIFO_WRITE_SCHEDULER_H_ +#define QUICHE_SPDY_CORE_FIFO_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_string_utils.h" + +namespace spdy { + +// A write scheduler where the stream with the smallest stream ID will have the +// highest priority. +template <typename StreamIdType> +class FifoWriteScheduler : public WriteScheduler<StreamIdType> { + public: + using typename WriteScheduler<StreamIdType>::StreamPrecedenceType; + + FifoWriteScheduler() = default; + + // WriteScheduler methods + void RegisterStream(StreamIdType stream_id, + const StreamPrecedenceType& precedence) override; + void UnregisterStream(StreamIdType stream_id) override; + bool StreamRegistered(StreamIdType stream_id) const override; + StreamPrecedenceType GetStreamPrecedence( + StreamIdType stream_id) const override; + void UpdateStreamPrecedence(StreamIdType stream_id, + const StreamPrecedenceType& precedence) override; + std::vector<StreamIdType> GetStreamChildren( + StreamIdType stream_id) const override; + void RecordStreamEventTime(StreamIdType stream_id, + int64_t now_in_usec) override; + int64_t GetLatestEventWithPrecedence(StreamIdType stream_id) const override; + bool ShouldYield(StreamIdType stream_id) const override; + void MarkStreamReady(StreamIdType stream_id, bool add_to_front) override; + void MarkStreamNotReady(StreamIdType stream_id) override; + bool HasReadyStreams() const override; + StreamIdType PopNextReadyStream() override; + std::tuple<StreamIdType, StreamPrecedenceType> + PopNextReadyStreamAndPrecedence() override; + size_t NumReadyStreams() const override; + bool IsStreamReady(StreamIdType stream_id) const override; + size_t NumRegisteredStreams() const override; + SpdyString DebugString() const override; + + private: + std::set<StreamIdType> ready_streams_; + // This map maps stream ID to read/write event time (us since Unix epoch). + std::map<StreamIdType, int64_t> registered_streams_; +}; + +template <typename StreamIdType> +void FifoWriteScheduler<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 FifoWriteScheduler<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> +bool FifoWriteScheduler<StreamIdType>::StreamRegistered( + StreamIdType stream_id) const { + return registered_streams_.find(stream_id) != registered_streams_.end(); +} + +// Stream precedence is not supported by this scheduler. +template <typename StreamIdType> +typename FifoWriteScheduler<StreamIdType>::StreamPrecedenceType +FifoWriteScheduler<StreamIdType>::GetStreamPrecedence( + StreamIdType /*stream_id*/) const { + return StreamPrecedenceType(kV3LowestPriority); +} + +template <typename StreamIdType> +void FifoWriteScheduler<StreamIdType>::UpdateStreamPrecedence( + StreamIdType /*stream_id*/, + const StreamPrecedenceType& /*precedence*/) {} + +template <typename StreamIdType> +std::vector<StreamIdType> FifoWriteScheduler<StreamIdType>::GetStreamChildren( + StreamIdType /*stream_id*/) const { + return std::vector<StreamIdType>(); +} + +template <typename StreamIdType> +void FifoWriteScheduler<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 FifoWriteScheduler<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_.begin(); it != registered_streams_.end(); + ++it) { + if (stream_id <= it->first) { + break; + } + latest_event_time_us = std::max(latest_event_time_us, it->second); + } + return latest_event_time_us; +} + +template <typename StreamIdType> +bool FifoWriteScheduler<StreamIdType>::ShouldYield( + StreamIdType stream_id) const { + return !ready_streams_.empty() && stream_id > *ready_streams_.begin(); +} + +template <typename StreamIdType> +void FifoWriteScheduler<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_DVLOG(1) << "Stream already exists in the list"; + return; + } + ready_streams_.insert(stream_id); +} + +template <typename StreamIdType> +void FifoWriteScheduler<StreamIdType>::MarkStreamNotReady( + StreamIdType stream_id) { + auto it = ready_streams_.find(stream_id); + if (it == ready_streams_.end()) { + SPDY_DVLOG(1) << "Try to remove a stream that is not on list"; + return; + } + ready_streams_.erase(it); +} + +template <typename StreamIdType> +bool FifoWriteScheduler<StreamIdType>::HasReadyStreams() const { + return !ready_streams_.empty(); +} + +template <typename StreamIdType> +StreamIdType FifoWriteScheduler<StreamIdType>::PopNextReadyStream() { + if (ready_streams_.empty()) { + SPDY_BUG << "No ready streams available"; + return 0; + } + auto it = ready_streams_.begin(); + StreamIdType id = *it; + ready_streams_.erase(it); + return id; +} + +template <typename StreamIdType> +std::tuple<StreamIdType, + typename FifoWriteScheduler<StreamIdType>::StreamPrecedenceType> +FifoWriteScheduler<StreamIdType>::PopNextReadyStreamAndPrecedence() { + return std::make_tuple(PopNextReadyStream(), + StreamPrecedenceType(kV3LowestPriority)); +} + +template <typename StreamIdType> +size_t FifoWriteScheduler<StreamIdType>::NumReadyStreams() const { + return ready_streams_.size(); +} + +template <typename StreamIdType> +bool FifoWriteScheduler<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 FifoWriteScheduler<StreamIdType>::NumRegisteredStreams() const { + return registered_streams_.size(); +} + +template <typename StreamIdType> +string FifoWriteScheduler<StreamIdType>::DebugString() const { + return SpdyStrCat( + "FifoWriteScheduler {num_streams=", registered_streams_.size(), + " num_ready_streams=", NumReadyStreams(), "}"); +} + +} // namespace spdy + +#endif // QUICHE_SPDY_CORE_FIFO_WRITE_SCHEDULER_H_
diff --git a/spdy/core/fifo_write_scheduler_test.cc b/spdy/core/fifo_write_scheduler_test.cc new file mode 100644 index 0000000..1a6e231 --- /dev/null +++ b/spdy/core/fifo_write_scheduler_test.cc
@@ -0,0 +1,85 @@ +// 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/fifo_write_scheduler.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 { + +TEST(FifoWriteSchedulerTest, BasicTest) { + FifoWriteScheduler<SpdyStreamId> fifo; + EXPECT_FALSE(fifo.HasReadyStreams()); + EXPECT_SPDY_BUG( + EXPECT_EQ(0, std::get<0>(fifo.PopNextReadyStreamAndPrecedence())), + "No ready streams available"); + EXPECT_SPDY_BUG(fifo.MarkStreamReady(9, true), "Stream 9 is not registered"); + EXPECT_SPDY_BUG(fifo.IsStreamReady(9), "Stream 9 is not registered"); + + SpdyStreamPrecedence precedence(1); + fifo.RegisterStream(3, precedence); + EXPECT_FALSE(fifo.IsStreamReady(3)); + fifo.RegisterStream(9, precedence); + fifo.RegisterStream(7, precedence); + fifo.RegisterStream(11, precedence); + fifo.RegisterStream(13, precedence); + fifo.RegisterStream(15, precedence); + fifo.RegisterStream(17, precedence); + EXPECT_EQ(7u, fifo.NumRegisteredStreams()); + EXPECT_FALSE(fifo.HasReadyStreams()); + + fifo.MarkStreamReady(9, true); + EXPECT_TRUE(fifo.IsStreamReady(9)); + EXPECT_TRUE(fifo.HasReadyStreams()); + fifo.MarkStreamReady(15, true); + fifo.MarkStreamReady(7, true); + fifo.MarkStreamReady(13, true); + fifo.MarkStreamReady(11, true); + fifo.MarkStreamReady(3, true); + fifo.MarkStreamReady(17, true); + EXPECT_EQ(7u, fifo.NumReadyStreams()); + + EXPECT_EQ(3, fifo.PopNextReadyStream()); + EXPECT_EQ(7, std::get<0>(fifo.PopNextReadyStreamAndPrecedence())); + EXPECT_EQ(5u, fifo.NumReadyStreams()); + + EXPECT_FALSE(fifo.ShouldYield(3)); + EXPECT_FALSE(fifo.ShouldYield(9)); + EXPECT_TRUE(fifo.ShouldYield(13)); + EXPECT_TRUE(fifo.ShouldYield(10)); + + fifo.MarkStreamNotReady(9); + EXPECT_EQ(4u, fifo.NumReadyStreams()); + EXPECT_FALSE(fifo.ShouldYield(10)); + EXPECT_TRUE(fifo.ShouldYield(12)); +} + +TEST(FifoWriteSchedulerTest, GetLatestEventTest) { + FifoWriteScheduler<SpdyStreamId> fifo; + + SpdyStreamPrecedence precedence(1); + fifo.RegisterStream(1, precedence); + fifo.RegisterStream(3, precedence); + fifo.RegisterStream(5, precedence); + fifo.RegisterStream(7, precedence); + fifo.RegisterStream(9, precedence); + fifo.RecordStreamEventTime(1, 3); + fifo.RecordStreamEventTime(3, 2); + fifo.RecordStreamEventTime(5, 4); + fifo.RecordStreamEventTime(7, 8); + fifo.RecordStreamEventTime(9, 1); + + EXPECT_EQ(8, fifo.GetLatestEventWithPrecedence(9)); + EXPECT_EQ(4, fifo.GetLatestEventWithPrecedence(7)); + EXPECT_EQ(3, fifo.GetLatestEventWithPrecedence(5)); + EXPECT_EQ(3, fifo.GetLatestEventWithPrecedence(3)); + EXPECT_EQ(0, fifo.GetLatestEventWithPrecedence(1)); +} + +} // namespace test + +} // namespace spdy