blob: 7488820d06dde197caae523d8e7058925c7c294b [file] [log] [blame]
QUICHE team82dee2f2019-01-18 12:35:12 -05001// Copyright (c) 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/third_party/quiche/src/spdy/core/priority_write_scheduler.h"
6
QUICHE team82dee2f2019-01-18 12:35:12 -05007#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
8#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
danzh8f3a5762019-06-25 13:43:51 -07009#include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h"
danzh57252942019-04-17 08:26:12 -070010#include "net/third_party/quiche/src/spdy/platform/api/spdy_test_helpers.h"
QUICHE team82dee2f2019-01-18 12:35:12 -050011
12namespace spdy {
13namespace test {
14
15template <typename StreamIdType>
16class PriorityWriteSchedulerPeer {
17 public:
18 explicit PriorityWriteSchedulerPeer(
19 PriorityWriteScheduler<StreamIdType>* scheduler)
20 : scheduler_(scheduler) {}
21
22 size_t NumReadyStreams(SpdyPriority priority) const {
23 return scheduler_->priority_infos_[priority].ready_list.size();
24 }
25
26 private:
27 PriorityWriteScheduler<StreamIdType>* scheduler_;
28};
29
30namespace {
31
32class PriorityWriteSchedulerTest : public ::testing::Test {
33 public:
34 PriorityWriteSchedulerTest() : peer_(&scheduler_) {}
35
36 PriorityWriteScheduler<SpdyStreamId> scheduler_;
37 PriorityWriteSchedulerPeer<SpdyStreamId> peer_;
38};
39
40TEST_F(PriorityWriteSchedulerTest, RegisterUnregisterStreams) {
41 EXPECT_FALSE(scheduler_.HasReadyStreams());
42 EXPECT_FALSE(scheduler_.StreamRegistered(1));
fayang753002d2019-07-24 11:54:43 -070043 EXPECT_EQ(0u, scheduler_.NumRegisteredStreams());
QUICHE team82dee2f2019-01-18 12:35:12 -050044 scheduler_.RegisterStream(1, SpdyStreamPrecedence(1));
45 EXPECT_TRUE(scheduler_.StreamRegistered(1));
fayang753002d2019-07-24 11:54:43 -070046 EXPECT_EQ(1u, scheduler_.NumRegisteredStreams());
QUICHE team82dee2f2019-01-18 12:35:12 -050047
48 // Root stream counts as already registered.
49 EXPECT_SPDY_BUG(
50 scheduler_.RegisterStream(kHttp2RootStreamId, SpdyStreamPrecedence(1)),
51 "Stream 0 already registered");
52
53 // Try redundant registrations.
54 EXPECT_SPDY_BUG(scheduler_.RegisterStream(1, SpdyStreamPrecedence(1)),
55 "Stream 1 already registered");
56 EXPECT_SPDY_BUG(scheduler_.RegisterStream(1, SpdyStreamPrecedence(2)),
57 "Stream 1 already registered");
58
59 scheduler_.RegisterStream(2, SpdyStreamPrecedence(3));
fayang753002d2019-07-24 11:54:43 -070060 EXPECT_EQ(2u, scheduler_.NumRegisteredStreams());
QUICHE team82dee2f2019-01-18 12:35:12 -050061
62 // Verify registration != ready.
63 EXPECT_FALSE(scheduler_.HasReadyStreams());
64
65 scheduler_.UnregisterStream(1);
fayang753002d2019-07-24 11:54:43 -070066 EXPECT_EQ(1u, scheduler_.NumRegisteredStreams());
QUICHE team82dee2f2019-01-18 12:35:12 -050067 scheduler_.UnregisterStream(2);
fayang753002d2019-07-24 11:54:43 -070068 EXPECT_EQ(0u, scheduler_.NumRegisteredStreams());
QUICHE team82dee2f2019-01-18 12:35:12 -050069
70 // Try redundant unregistration.
71 EXPECT_SPDY_BUG(scheduler_.UnregisterStream(1), "Stream 1 not registered");
72 EXPECT_SPDY_BUG(scheduler_.UnregisterStream(2), "Stream 2 not registered");
73}
74
75TEST_F(PriorityWriteSchedulerTest, RegisterStreamWithHttp2StreamDependency) {
76 EXPECT_FALSE(scheduler_.HasReadyStreams());
77 EXPECT_FALSE(scheduler_.StreamRegistered(1));
78 scheduler_.RegisterStream(
79 1, SpdyStreamPrecedence(kHttp2RootStreamId, 123, false));
80 EXPECT_TRUE(scheduler_.StreamRegistered(1));
81 EXPECT_TRUE(scheduler_.GetStreamPrecedence(1).is_spdy3_priority());
82 EXPECT_EQ(3, scheduler_.GetStreamPrecedence(1).spdy3_priority());
83 EXPECT_FALSE(scheduler_.HasReadyStreams());
84
85 EXPECT_SPDY_BUG(scheduler_.RegisterStream(
86 1, SpdyStreamPrecedence(kHttp2RootStreamId, 256, false)),
87 "Stream 1 already registered");
88 EXPECT_TRUE(scheduler_.GetStreamPrecedence(1).is_spdy3_priority());
89 EXPECT_EQ(3, scheduler_.GetStreamPrecedence(1).spdy3_priority());
90
91 // Registering stream with a non-existent parent stream is permissible, per
92 // b/15676312, but parent stream will always be reset to 0.
93 scheduler_.RegisterStream(2, SpdyStreamPrecedence(3, 123, false));
94 EXPECT_TRUE(scheduler_.StreamRegistered(2));
95 EXPECT_FALSE(scheduler_.StreamRegistered(3));
96 EXPECT_EQ(kHttp2RootStreamId, scheduler_.GetStreamPrecedence(2).parent_id());
97}
98
99TEST_F(PriorityWriteSchedulerTest, GetStreamPrecedence) {
100 // Unknown streams tolerated due to b/15676312. However, return lowest
101 // priority.
102 EXPECT_EQ(kV3LowestPriority,
103 scheduler_.GetStreamPrecedence(1).spdy3_priority());
104
105 scheduler_.RegisterStream(1, SpdyStreamPrecedence(3));
106 EXPECT_TRUE(scheduler_.GetStreamPrecedence(1).is_spdy3_priority());
107 EXPECT_EQ(3, scheduler_.GetStreamPrecedence(1).spdy3_priority());
108
109 // Redundant registration shouldn't change stream priority.
110 EXPECT_SPDY_BUG(scheduler_.RegisterStream(1, SpdyStreamPrecedence(4)),
111 "Stream 1 already registered");
112 EXPECT_EQ(3, scheduler_.GetStreamPrecedence(1).spdy3_priority());
113
114 scheduler_.UpdateStreamPrecedence(1, SpdyStreamPrecedence(5));
115 EXPECT_EQ(5, scheduler_.GetStreamPrecedence(1).spdy3_priority());
116
117 // Toggling ready state shouldn't change stream priority.
118 scheduler_.MarkStreamReady(1, true);
119 EXPECT_EQ(5, scheduler_.GetStreamPrecedence(1).spdy3_priority());
120
121 // Test changing priority of ready stream.
122 EXPECT_EQ(1u, peer_.NumReadyStreams(5));
123 scheduler_.UpdateStreamPrecedence(1, SpdyStreamPrecedence(6));
124 EXPECT_EQ(6, scheduler_.GetStreamPrecedence(1).spdy3_priority());
125 EXPECT_EQ(0u, peer_.NumReadyStreams(5));
126 EXPECT_EQ(1u, peer_.NumReadyStreams(6));
127
128 EXPECT_EQ(1u, scheduler_.PopNextReadyStream());
129 EXPECT_EQ(6, scheduler_.GetStreamPrecedence(1).spdy3_priority());
130
131 scheduler_.UnregisterStream(1);
132 EXPECT_EQ(kV3LowestPriority,
133 scheduler_.GetStreamPrecedence(1).spdy3_priority());
134}
135
136TEST_F(PriorityWriteSchedulerTest, PopNextReadyStreamAndPrecedence) {
137 scheduler_.RegisterStream(1, SpdyStreamPrecedence(3));
138 scheduler_.MarkStreamReady(1, true);
139 EXPECT_EQ(std::make_tuple(1u, SpdyStreamPrecedence(3)),
140 scheduler_.PopNextReadyStreamAndPrecedence());
141 scheduler_.UnregisterStream(1);
142}
143
144TEST_F(PriorityWriteSchedulerTest, UpdateStreamPrecedence) {
145 // For the moment, updating stream precedence on a non-registered stream
146 // should have no effect. In the future, it will lazily cause the stream to
147 // be registered (b/15676312).
148 EXPECT_EQ(kV3LowestPriority,
149 scheduler_.GetStreamPrecedence(3).spdy3_priority());
150 EXPECT_FALSE(scheduler_.StreamRegistered(3));
151 scheduler_.UpdateStreamPrecedence(3, SpdyStreamPrecedence(1));
152 EXPECT_FALSE(scheduler_.StreamRegistered(3));
153 EXPECT_EQ(kV3LowestPriority,
154 scheduler_.GetStreamPrecedence(3).spdy3_priority());
155
156 scheduler_.RegisterStream(3, SpdyStreamPrecedence(1));
157 EXPECT_EQ(1, scheduler_.GetStreamPrecedence(3).spdy3_priority());
158 scheduler_.UpdateStreamPrecedence(3, SpdyStreamPrecedence(2));
159 EXPECT_EQ(2, scheduler_.GetStreamPrecedence(3).spdy3_priority());
160
161 // Updating priority of stream to current priority value is valid, but has no
162 // effect.
163 scheduler_.UpdateStreamPrecedence(3, SpdyStreamPrecedence(2));
164 EXPECT_EQ(2, scheduler_.GetStreamPrecedence(3).spdy3_priority());
165
166 // Even though stream 4 is marked ready after stream 5, it should be returned
167 // first by PopNextReadyStream() since it has higher priority.
168 scheduler_.RegisterStream(4, SpdyStreamPrecedence(1));
169 scheduler_.MarkStreamReady(3, false); // priority 2
170 EXPECT_TRUE(scheduler_.IsStreamReady(3));
171 scheduler_.MarkStreamReady(4, false); // priority 1
172 EXPECT_TRUE(scheduler_.IsStreamReady(4));
173 EXPECT_EQ(4u, scheduler_.PopNextReadyStream());
174 EXPECT_FALSE(scheduler_.IsStreamReady(4));
175 EXPECT_EQ(3u, scheduler_.PopNextReadyStream());
176 EXPECT_FALSE(scheduler_.IsStreamReady(3));
177
178 // Verify that lowering priority of stream 4 causes it to be returned later
179 // by PopNextReadyStream().
180 scheduler_.MarkStreamReady(3, false); // priority 2
181 scheduler_.MarkStreamReady(4, false); // priority 1
182 scheduler_.UpdateStreamPrecedence(4, SpdyStreamPrecedence(3));
183 EXPECT_EQ(3u, scheduler_.PopNextReadyStream());
184 EXPECT_EQ(4u, scheduler_.PopNextReadyStream());
185
186 scheduler_.UnregisterStream(3);
187}
188
189TEST_F(PriorityWriteSchedulerTest,
190 UpdateStreamPrecedenceWithHttp2StreamDependency) {
191 // Unknown streams tolerated due to b/15676312, but should have no effect.
192 scheduler_.UpdateStreamPrecedence(3, SpdyStreamPrecedence(0, 100, false));
193 EXPECT_FALSE(scheduler_.StreamRegistered(3));
194
195 scheduler_.RegisterStream(3, SpdyStreamPrecedence(3));
196 scheduler_.UpdateStreamPrecedence(3, SpdyStreamPrecedence(0, 100, false));
197 EXPECT_TRUE(scheduler_.GetStreamPrecedence(3).is_spdy3_priority());
198 EXPECT_EQ(4, scheduler_.GetStreamPrecedence(3).spdy3_priority());
199
200 scheduler_.UnregisterStream(3);
201 scheduler_.UpdateStreamPrecedence(3, SpdyStreamPrecedence(0, 100, false));
202 EXPECT_FALSE(scheduler_.StreamRegistered(3));
203}
204
205TEST_F(PriorityWriteSchedulerTest, MarkStreamReadyBack) {
206 EXPECT_FALSE(scheduler_.HasReadyStreams());
207 EXPECT_SPDY_BUG(scheduler_.MarkStreamReady(1, false),
208 "Stream 1 not registered");
209 EXPECT_FALSE(scheduler_.HasReadyStreams());
210 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
211 "No ready streams available");
212
213 // Add a bunch of ready streams to tail of per-priority lists.
214 // Expected order: (P2) 4, (P3) 1, 2, 3, (P5) 5.
215 scheduler_.RegisterStream(1, SpdyStreamPrecedence(3));
216 scheduler_.MarkStreamReady(1, false);
217 EXPECT_TRUE(scheduler_.HasReadyStreams());
218 scheduler_.RegisterStream(2, SpdyStreamPrecedence(3));
219 scheduler_.MarkStreamReady(2, false);
220 scheduler_.RegisterStream(3, SpdyStreamPrecedence(3));
221 scheduler_.MarkStreamReady(3, false);
222 scheduler_.RegisterStream(4, SpdyStreamPrecedence(2));
223 scheduler_.MarkStreamReady(4, false);
224 scheduler_.RegisterStream(5, SpdyStreamPrecedence(5));
225 scheduler_.MarkStreamReady(5, false);
226
227 EXPECT_EQ(4u, scheduler_.PopNextReadyStream());
228 EXPECT_EQ(1u, scheduler_.PopNextReadyStream());
229 EXPECT_EQ(2u, scheduler_.PopNextReadyStream());
230 EXPECT_EQ(3u, scheduler_.PopNextReadyStream());
231 EXPECT_EQ(5u, scheduler_.PopNextReadyStream());
232 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
233 "No ready streams available");
234}
235
236TEST_F(PriorityWriteSchedulerTest, MarkStreamReadyFront) {
237 EXPECT_FALSE(scheduler_.HasReadyStreams());
238 EXPECT_SPDY_BUG(scheduler_.MarkStreamReady(1, true),
239 "Stream 1 not registered");
240 EXPECT_FALSE(scheduler_.HasReadyStreams());
241 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
242 "No ready streams available");
243
244 // Add a bunch of ready streams to head of per-priority lists.
245 // Expected order: (P2) 4, (P3) 3, 2, 1, (P5) 5
246 scheduler_.RegisterStream(1, SpdyStreamPrecedence(3));
247 scheduler_.MarkStreamReady(1, true);
248 EXPECT_TRUE(scheduler_.HasReadyStreams());
249 scheduler_.RegisterStream(2, SpdyStreamPrecedence(3));
250 scheduler_.MarkStreamReady(2, true);
251 scheduler_.RegisterStream(3, SpdyStreamPrecedence(3));
252 scheduler_.MarkStreamReady(3, true);
253 scheduler_.RegisterStream(4, SpdyStreamPrecedence(2));
254 scheduler_.MarkStreamReady(4, true);
255 scheduler_.RegisterStream(5, SpdyStreamPrecedence(5));
256 scheduler_.MarkStreamReady(5, true);
257
258 EXPECT_EQ(4u, scheduler_.PopNextReadyStream());
259 EXPECT_EQ(3u, scheduler_.PopNextReadyStream());
260 EXPECT_EQ(2u, scheduler_.PopNextReadyStream());
261 EXPECT_EQ(1u, scheduler_.PopNextReadyStream());
262 EXPECT_EQ(5u, scheduler_.PopNextReadyStream());
263 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
264 "No ready streams available");
265}
266
267TEST_F(PriorityWriteSchedulerTest, MarkStreamReadyBackAndFront) {
268 scheduler_.RegisterStream(1, SpdyStreamPrecedence(4));
269 scheduler_.RegisterStream(2, SpdyStreamPrecedence(3));
270 scheduler_.RegisterStream(3, SpdyStreamPrecedence(3));
271 scheduler_.RegisterStream(4, SpdyStreamPrecedence(3));
272 scheduler_.RegisterStream(5, SpdyStreamPrecedence(4));
273 scheduler_.RegisterStream(6, SpdyStreamPrecedence(1));
274
275 // Add a bunch of ready streams to per-priority lists, with variety of adding
276 // at head and tail.
277 // Expected order: (P1) 6, (P3) 4, 2, 3, (P4) 1, 5
278 scheduler_.MarkStreamReady(1, true);
279 scheduler_.MarkStreamReady(2, true);
280 scheduler_.MarkStreamReady(3, false);
281 scheduler_.MarkStreamReady(4, true);
282 scheduler_.MarkStreamReady(5, false);
283 scheduler_.MarkStreamReady(6, true);
284
285 EXPECT_EQ(6u, scheduler_.PopNextReadyStream());
286 EXPECT_EQ(4u, scheduler_.PopNextReadyStream());
287 EXPECT_EQ(2u, scheduler_.PopNextReadyStream());
288 EXPECT_EQ(3u, scheduler_.PopNextReadyStream());
289 EXPECT_EQ(1u, scheduler_.PopNextReadyStream());
290 EXPECT_EQ(5u, scheduler_.PopNextReadyStream());
291 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
292 "No ready streams available");
293}
294
295TEST_F(PriorityWriteSchedulerTest, MarkStreamNotReady) {
296 // Verify ready state reflected in NumReadyStreams().
297 scheduler_.RegisterStream(1, SpdyStreamPrecedence(1));
298 EXPECT_EQ(0u, scheduler_.NumReadyStreams());
299 scheduler_.MarkStreamReady(1, false);
300 EXPECT_EQ(1u, scheduler_.NumReadyStreams());
301 scheduler_.MarkStreamNotReady(1);
302 EXPECT_EQ(0u, scheduler_.NumReadyStreams());
303
304 // Empty pop should fail.
305 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
306 "No ready streams available");
307
308 // Tolerate redundant marking of a stream as not ready.
309 scheduler_.MarkStreamNotReady(1);
310 EXPECT_EQ(0u, scheduler_.NumReadyStreams());
311
312 // Should only be able to mark registered streams.
313 EXPECT_SPDY_BUG(scheduler_.MarkStreamNotReady(3), "Stream 3 not registered");
314}
315
316TEST_F(PriorityWriteSchedulerTest, UnregisterRemovesStream) {
317 scheduler_.RegisterStream(3, SpdyStreamPrecedence(4));
318 scheduler_.MarkStreamReady(3, false);
319 EXPECT_EQ(1u, scheduler_.NumReadyStreams());
320
321 // Unregistering a stream should remove it from set of ready streams.
322 scheduler_.UnregisterStream(3);
323 EXPECT_EQ(0u, scheduler_.NumReadyStreams());
324 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
325 "No ready streams available");
326}
327
328TEST_F(PriorityWriteSchedulerTest, ShouldYield) {
329 scheduler_.RegisterStream(1, SpdyStreamPrecedence(1));
330 scheduler_.RegisterStream(4, SpdyStreamPrecedence(4));
331 scheduler_.RegisterStream(5, SpdyStreamPrecedence(4));
332 scheduler_.RegisterStream(7, SpdyStreamPrecedence(7));
333
334 // Make sure we don't yield when the list is empty.
335 EXPECT_FALSE(scheduler_.ShouldYield(1));
336
337 // Add a low priority stream.
338 scheduler_.MarkStreamReady(4, false);
339 // 4 should not yield to itself.
340 EXPECT_FALSE(scheduler_.ShouldYield(4));
341 // 7 should yield as 4 is blocked and a higher priority.
342 EXPECT_TRUE(scheduler_.ShouldYield(7));
343 // 5 should yield to 4 as they are the same priority.
344 EXPECT_TRUE(scheduler_.ShouldYield(5));
345 // 1 should not yield as 1 is higher priority.
346 EXPECT_FALSE(scheduler_.ShouldYield(1));
347
348 // Add a second stream in that priority class.
349 scheduler_.MarkStreamReady(5, false);
350 // 4 and 5 are both blocked, but 4 is at the front so should not yield.
351 EXPECT_FALSE(scheduler_.ShouldYield(4));
352 EXPECT_TRUE(scheduler_.ShouldYield(5));
353}
354
355TEST_F(PriorityWriteSchedulerTest, GetLatestEventWithPrecedence) {
356 EXPECT_SPDY_BUG(scheduler_.RecordStreamEventTime(3, 5),
357 "Stream 3 not registered");
358 EXPECT_SPDY_BUG(EXPECT_EQ(0, scheduler_.GetLatestEventWithPrecedence(4)),
359 "Stream 4 not registered");
360
361 for (int i = 1; i < 5; ++i) {
362 scheduler_.RegisterStream(i, SpdyStreamPrecedence(i));
363 }
364 for (int i = 1; i < 5; ++i) {
365 EXPECT_EQ(0, scheduler_.GetLatestEventWithPrecedence(i));
366 }
367 for (int i = 1; i < 5; ++i) {
368 scheduler_.RecordStreamEventTime(i, i * 100);
369 }
370 for (int i = 1; i < 5; ++i) {
371 EXPECT_EQ((i - 1) * 100, scheduler_.GetLatestEventWithPrecedence(i));
372 }
373}
374
375} // namespace
376} // namespace test
377} // namespace spdy