gfe-relnote: Fix Http2PriorityWriteScheduler::ShouldYield. Not used in prod. Not protected. Current ShouldYield() assumes the passed in stream_id has ready==true, and if ready==false, it will yield to any other stream. This change fixes ShouldYield() so that it returns correct answers even if ready==false for the given stream_id. PiperOrigin-RevId: 260147911 Change-Id: Ieefb5fc3a341c498bb1f7bf89cae0f92db8691ac
diff --git a/spdy/core/http2_priority_write_scheduler.h b/spdy/core/http2_priority_write_scheduler.h index d08d125..a35e076 100644 --- a/spdy/core/http2_priority_write_scheduler.h +++ b/spdy/core/http2_priority_write_scheduler.h
@@ -111,6 +111,17 @@ // Time of latest write event for stream of this priority, in microseconds. int64_t last_event_time_usec = 0; + // Returns true if this stream is ancestor of |other|. + bool IsAncestorOf(const StreamInfo& other) const { + for (const StreamInfo* parent = other.parent; parent != nullptr; + parent = parent->parent) { + if (parent == this) { + return true; + } + } + return false; + } + // Whether this stream should be scheduled ahead of another stream. bool SchedulesBefore(const StreamInfo& other) const { return (priority != other.priority) ? priority > other.priority @@ -500,13 +511,20 @@ SPDY_BUG << "Stream " << stream_id << " not registered"; return false; } + if (HasReadyAncestor(*stream_info)) { + return true; + } for (const StreamInfo& scheduled : scheduling_queue_) { - if (stream_info == &scheduled) { + if (HasReadyAncestor(scheduled)) { + // Skip streams which cannot be scheduled. + continue; + } + if (stream_info->IsAncestorOf(scheduled)) { + // Do not yield to descendants. return false; } - if (!HasReadyAncestor(scheduled)) { - return true; - } + // Yield to streams with higher priorities. + return scheduled.SchedulesBefore(*stream_info); } return false; }
diff --git a/spdy/core/http2_priority_write_scheduler_test.cc b/spdy/core/http2_priority_write_scheduler_test.cc index 484e59a..e498c37 100644 --- a/spdy/core/http2_priority_write_scheduler_test.cc +++ b/spdy/core/http2_priority_write_scheduler_test.cc
@@ -653,6 +653,46 @@ scheduler_.PopNextReadyStreamAndPrecedence()); } +TEST_F(Http2PriorityWriteSchedulerTest, ShouldYield) { + /* + 0 + /|\ + 1 2 3 + /|\ \ + 4 5 6 7 + | + 8 + + */ + scheduler_.RegisterStream(1, SpdyStreamPrecedence(0, 100, false)); + scheduler_.RegisterStream(2, SpdyStreamPrecedence(0, 100, false)); + scheduler_.RegisterStream(3, SpdyStreamPrecedence(0, 100, false)); + scheduler_.RegisterStream(4, SpdyStreamPrecedence(1, 100, false)); + scheduler_.RegisterStream(5, SpdyStreamPrecedence(1, 200, false)); + scheduler_.RegisterStream(6, SpdyStreamPrecedence(1, 255, false)); + scheduler_.RegisterStream(7, SpdyStreamPrecedence(2, 100, false)); + scheduler_.RegisterStream(8, SpdyStreamPrecedence(5, 100, false)); + + scheduler_.MarkStreamReady(5, false); + + for (int i = 1; i <= 8; ++i) { + // Verify only 4 and 8 should yield to 5. + if (i == 4 || i == 8) { + EXPECT_TRUE(scheduler_.ShouldYield(i)) << "stream_id: " << i; + } else { + EXPECT_FALSE(scheduler_.ShouldYield(i)) << "stream_id: " << i; + } + } + + // Marks streams 1 and 2 ready. + scheduler_.MarkStreamReady(1, false); + scheduler_.MarkStreamReady(2, false); + // 1 should not yield. + EXPECT_FALSE(scheduler_.ShouldYield(1)); + // Verify 2 should yield to 1. + EXPECT_TRUE(scheduler_.ShouldYield(2)); +} + class PopNextReadyStreamTest : public Http2PriorityWriteSchedulerTest { protected: void SetUp() override {