Add minimal spdy priority set by gws response header for initial cwnd bootstrapping for http2. also store spdy priority within spdydispatcher::sessiondata. no production behavior change unless we start an additional gws experiment. protected by gfe2_restart_flag_h2_adjust_initial_cwnd_by_gws. While QUIC stores SPDY within spdy::PriorityWriteScheduler<...>, SPDY priority is stored in a map from stream_id to SessionData owned by SpdyClientDispatcher in this change because (1) H2 uses LIFOWriteScheduler<...> by default, which ignores precedence (http://shortn/_2Ny3X9xBh6). (2) We only need CWND bootstrapping for traffic from client, and hence only SpdyClientDispatcher populates the SPDY priority. PiperOrigin-RevId: 316117083 Change-Id: I232e421b16edecc5f7146ce4fb36592a71a7e86f
diff --git a/spdy/core/fifo_write_scheduler.h b/spdy/core/fifo_write_scheduler.h index a2c6989..7b31891 100644 --- a/spdy/core/fifo_write_scheduler.h +++ b/spdy/core/fifo_write_scheduler.h
@@ -29,6 +29,8 @@ const StreamPrecedenceType& precedence) override; void UnregisterStream(StreamIdType stream_id) override; bool StreamRegistered(StreamIdType stream_id) const override; + // Stream precedence is available but note that it is not used for scheduling + // in this scheduler. StreamPrecedenceType GetStreamPrecedence( StreamIdType stream_id) const override; void UpdateStreamPrecedence(StreamIdType stream_id, @@ -51,20 +53,26 @@ std::string DebugString() const override; private: + struct StreamInfo { + SpdyPriority priority; + int64_t event_time; // read/write event time (us since Unix epoch). + }; + 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_; + std::map<StreamIdType, StreamInfo> registered_streams_; }; template <typename StreamIdType> void FifoWriteScheduler<StreamIdType>::RegisterStream( StreamIdType stream_id, - const StreamPrecedenceType& /*precedence*/) { + 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); + registered_streams_.emplace_hint( + registered_streams_.end(), stream_id, + StreamInfo{/*priority=*/precedence.spdy3_priority(), /*event_time=*/0}); } template <typename StreamIdType> @@ -88,14 +96,26 @@ template <typename StreamIdType> typename FifoWriteScheduler<StreamIdType>::StreamPrecedenceType FifoWriteScheduler<StreamIdType>::GetStreamPrecedence( - StreamIdType /*stream_id*/) const { - return StreamPrecedenceType(kV3LowestPriority); + StreamIdType stream_id) const { + auto it = registered_streams_.find(stream_id); + if (it == registered_streams_.end()) { + SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; + return StreamPrecedenceType(kV3LowestPriority); + } + return StreamPrecedenceType(it->second.priority); } template <typename StreamIdType> void FifoWriteScheduler<StreamIdType>::UpdateStreamPrecedence( - StreamIdType /*stream_id*/, - const StreamPrecedenceType& /*precedence*/) {} + StreamIdType stream_id, + const StreamPrecedenceType& precedence) { + auto it = registered_streams_.find(stream_id); + if (it == registered_streams_.end()) { + SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; + return; + } + it->second.priority = precedence.spdy3_priority(); +} template <typename StreamIdType> std::vector<StreamIdType> FifoWriteScheduler<StreamIdType>::GetStreamChildren( @@ -109,7 +129,7 @@ int64_t now_in_usec) { auto it = registered_streams_.find(stream_id); if (it != registered_streams_.end()) { - it->second = now_in_usec; + it->second.event_time = now_in_usec; } else { SPDY_BUG << "Stream " << stream_id << " is not registered"; } @@ -128,7 +148,8 @@ if (stream_id <= it->first) { break; } - latest_event_time_us = std::max(latest_event_time_us, it->second); + latest_event_time_us = + std::max(latest_event_time_us, it->second.event_time); } return latest_event_time_us; }
diff --git a/spdy/core/fifo_write_scheduler_test.cc b/spdy/core/fifo_write_scheduler_test.cc index 950b641..d1dd066 100644 --- a/spdy/core/fifo_write_scheduler_test.cc +++ b/spdy/core/fifo_write_scheduler_test.cc
@@ -80,6 +80,24 @@ EXPECT_EQ(0, fifo.GetLatestEventWithPrecedence(1)); } +TEST(FifoWriteSchedulerTest, GetStreamPrecedence) { + FifoWriteScheduler<SpdyStreamId> fifo; + // Return lowest priority for unknown stream. + EXPECT_EQ(kV3LowestPriority, fifo.GetStreamPrecedence(1).spdy3_priority()); + + fifo.RegisterStream(1, SpdyStreamPrecedence(3)); + EXPECT_TRUE(fifo.GetStreamPrecedence(1).is_spdy3_priority()); + EXPECT_EQ(3, fifo.GetStreamPrecedence(1).spdy3_priority()); + + // Redundant registration shouldn't change stream priority. + EXPECT_SPDY_BUG(fifo.RegisterStream(1, SpdyStreamPrecedence(4)), + "Stream 1 already registered"); + EXPECT_EQ(3, fifo.GetStreamPrecedence(1).spdy3_priority()); + + fifo.UpdateStreamPrecedence(1, SpdyStreamPrecedence(5)); + EXPECT_EQ(5, fifo.GetStreamPrecedence(1).spdy3_priority()); +} + } // namespace test } // namespace spdy
diff --git a/spdy/core/lifo_write_scheduler.h b/spdy/core/lifo_write_scheduler.h index 405ccf5..d2e7fcf 100644 --- a/spdy/core/lifo_write_scheduler.h +++ b/spdy/core/lifo_write_scheduler.h
@@ -5,6 +5,7 @@ #ifndef QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_ #define QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_ +#include <cstdint> #include <map> #include <set> #include <string> @@ -40,15 +41,13 @@ return registered_streams_.find(stream_id) != registered_streams_.end(); } - // Stream precedence is not supported by this scheduler. + // Stream precedence is available but note that it is not used for scheduling + // in this scheduler. StreamPrecedenceType GetStreamPrecedence( - StreamIdType /*stream_id*/) const override { - return StreamPrecedenceType(kV3LowestPriority); - } + StreamIdType stream_id) const override; - void UpdateStreamPrecedence( - StreamIdType /*stream_id*/, - const StreamPrecedenceType& /*precedence*/) override {} + void UpdateStreamPrecedence(StreamIdType stream_id, + const StreamPrecedenceType& precedence) override; std::vector<StreamIdType> GetStreamChildren( StreamIdType /*stream_id*/) const override { @@ -85,19 +84,26 @@ private: friend class test::LifoWriteSchedulerPeer<StreamIdType>; + struct StreamInfo { + SpdyPriority priority; + int64_t event_time; // read/write event time (us since Unix epoch). + }; + std::set<StreamIdType> ready_streams_; - std::map<StreamIdType, int64_t> registered_streams_; + std::map<StreamIdType, StreamInfo> registered_streams_; }; template <typename StreamIdType> void LifoWriteScheduler<StreamIdType>::RegisterStream( StreamIdType stream_id, - const StreamPrecedenceType& /*precedence*/) { + 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); + registered_streams_.emplace_hint( + registered_streams_.end(), stream_id, + StreamInfo{/*priority=*/precedence.spdy3_priority(), /*event_time=*/0}); } template <typename StreamIdType> @@ -112,12 +118,36 @@ } template <typename StreamIdType> +typename LifoWriteScheduler<StreamIdType>::StreamPrecedenceType +LifoWriteScheduler<StreamIdType>::GetStreamPrecedence( + StreamIdType stream_id) const { + auto it = registered_streams_.find(stream_id); + if (it == registered_streams_.end()) { + SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; + return StreamPrecedenceType(kV3LowestPriority); + } + return StreamPrecedenceType(it->second.priority); +} + +template <typename StreamIdType> +void LifoWriteScheduler<StreamIdType>::UpdateStreamPrecedence( + StreamIdType stream_id, + const StreamPrecedenceType& precedence) { + auto it = registered_streams_.find(stream_id); + if (it == registered_streams_.end()) { + SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; + return; + } + it->second.priority = precedence.spdy3_priority(); +} + +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; + it->second.event_time = now_in_usec; } else { SPDY_BUG << "Stream " << stream_id << " is not registered"; } @@ -134,8 +164,8 @@ 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; + if (it->second.event_time > latest_event_time_us) { + latest_event_time_us = it->second.event_time; } } else { break;
diff --git a/spdy/core/lifo_write_scheduler_test.cc b/spdy/core/lifo_write_scheduler_test.cc index 1d7ecbf..744ee13 100644 --- a/spdy/core/lifo_write_scheduler_test.cc +++ b/spdy/core/lifo_write_scheduler_test.cc
@@ -151,6 +151,24 @@ "Stream 11 is not registered"); } +TEST(LifoWriteSchedulerTest, GetStreamPrecedence) { + LifoWriteScheduler<SpdyStreamId> lifo; + // Return lowest priority for unknown stream. + EXPECT_EQ(kV3LowestPriority, lifo.GetStreamPrecedence(1).spdy3_priority()); + + lifo.RegisterStream(1, SpdyStreamPrecedence(3)); + EXPECT_TRUE(lifo.GetStreamPrecedence(1).is_spdy3_priority()); + EXPECT_EQ(3, lifo.GetStreamPrecedence(1).spdy3_priority()); + + // Redundant registration shouldn't change stream priority. + EXPECT_SPDY_BUG(lifo.RegisterStream(1, SpdyStreamPrecedence(4)), + "Stream 1 already registered"); + EXPECT_EQ(3, lifo.GetStreamPrecedence(1).spdy3_priority()); + + lifo.UpdateStreamPrecedence(1, SpdyStreamPrecedence(5)); + EXPECT_EQ(5, lifo.GetStreamPrecedence(1).spdy3_priority()); +} + } // namespace test } // namespace spdy