1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree.
7  */
8 
9 #include <list>
10 #include <map>
11 #include <thread>
12 
13 #include <folly/Random.h>
14 #include <folly/io/async/test/MockTimeoutManager.h>
15 #include <folly/io/async/test/UndelayedDestruction.h>
16 #include <folly/portability/GTest.h>
17 #include <proxygen/lib/http/session/HTTP2PriorityQueue.h>
18 
19 using namespace std::placeholders;
20 using namespace testing;
21 using folly::HHWheelTimer;
22 using folly::Random;
23 using folly::test::MockTimeoutManager;
24 
25 namespace {
26 static char* fakeTxn = (char*)0xface0000;
27 
28 const proxygen::HTTPCodec::StreamID kRootNodeId =
29     std::numeric_limits<uint64_t>::max();
30 
makeFakeTxn(proxygen::HTTPCodec::StreamID id)31 proxygen::HTTPTransaction* makeFakeTxn(proxygen::HTTPCodec::StreamID id) {
32   return (proxygen::HTTPTransaction*)(fakeTxn + id);
33 }
34 
getTxnID(proxygen::HTTPTransaction * txn)35 proxygen::HTTPCodec::StreamID getTxnID(proxygen::HTTPTransaction* txn) {
36   return (proxygen::HTTPCodec::StreamID)((char*)txn - fakeTxn);
37 }
38 
39 // folly::Random::rand32 is broken because it takes RNG by value
rand32(uint32_t max,folly::Random::DefaultGenerator & rng)40 uint32_t rand32(uint32_t max, folly::Random::DefaultGenerator& rng) {
41   if (max == 0) {
42     return 0;
43   }
44 
45   return std::uniform_int_distribution<uint32_t>(0, max - 1)(rng);
46 }
47 
48 } // namespace
49 
50 namespace proxygen {
51 
52 using IDList = std::list<std::pair<HTTPCodec::StreamID, uint8_t>>;
53 
54 class QueueTest : public testing::Test {
55  public:
QueueTest(HHWheelTimer * timer=nullptr)56   explicit QueueTest(HHWheelTimer* timer = nullptr)
57       : q_(WheelTimerInstance(timer), kRootNodeId) {
58   }
59 
60  protected:
addTransaction(HTTPCodec::StreamID id,http2::PriorityUpdate pri,bool pnode=false,uint64_t * depth=nullptr)61   void addTransaction(HTTPCodec::StreamID id,
62                       http2::PriorityUpdate pri,
63                       bool pnode = false,
64                       uint64_t* depth = nullptr) {
65     HTTP2PriorityQueue::Handle h = q_.addTransaction(
66         id, pri, pnode ? nullptr : makeFakeTxn(id), false, depth);
67     // Blow away the old handle.  Hopefully the caller knows what they are doing
68     handles_.erase(id);
69     handles_.insert(std::make_pair(id, h));
70     if (!pnode) {
71       signalEgress(id, 1);
72     }
73   }
74 
removeTransaction(HTTPCodec::StreamID id)75   void removeTransaction(HTTPCodec::StreamID id) {
76     q_.removeTransaction(handles_[id]);
77   }
78 
updatePriority(HTTPCodec::StreamID id,http2::PriorityUpdate pri,uint64_t * depth=nullptr)79   void updatePriority(HTTPCodec::StreamID id,
80                       http2::PriorityUpdate pri,
81                       uint64_t* depth = nullptr) {
82     handles_[id] = q_.updatePriority(handles_[id], pri, depth);
83   }
84 
signalEgress(HTTPCodec::StreamID id,bool mark)85   void signalEgress(HTTPCodec::StreamID id, bool mark) {
86     if (mark) {
87       q_.signalPendingEgress(handles_[id]);
88     } else {
89       q_.clearPendingEgress(handles_[id]);
90     }
91   }
92 
buildSimpleTree()93   void buildSimpleTree() {
94     addTransaction(0, {kRootNodeId, false, 15});
95     addTransaction(3, {0, false, 3});
96     addTransaction(5, {0, false, 3});
97     addTransaction(7, {0, false, 7});
98     addTransaction(9, {5, false, 7});
99   }
100 
visitNode(HTTP2PriorityQueue &,HTTPCodec::StreamID id,HTTPTransaction *,double r)101   bool visitNode(HTTP2PriorityQueue&,
102                  HTTPCodec::StreamID id,
103                  HTTPTransaction*,
104                  double r) {
105     nodes_.push_back(std::make_pair(id, r * 100));
106     return false;
107   }
108 
dump()109   void dump() {
110     nodes_.clear();
111     q_.iterate(
112         std::bind(&QueueTest::visitNode, this, std::ref(q_), _1, _2, _3),
113         [] { return false; },
114         true);
115   }
116 
dumpBFS(const std::function<bool ()> & stopFn)117   void dumpBFS(const std::function<bool()>& stopFn) {
118     nodes_.clear();
119     q_.iterateBFS(
120         std::bind(&QueueTest::visitNode, this, _1, _2, _3, _4), stopFn, true);
121   }
122 
nextEgress(bool spdyMode=false)123   void nextEgress(bool spdyMode = false) {
124     HTTP2PriorityQueue::NextEgressResult nextEgressResults;
125     q_.nextEgress(nextEgressResults, spdyMode);
126     nodes_.clear();
127     for (auto p : nextEgressResults) {
128       nodes_.push_back(std::make_pair(getTxnID(p.first), p.second * 100));
129     }
130   }
131 
132   HTTP2PriorityQueue q_;
133   std::map<HTTPCodec::StreamID, HTTP2PriorityQueue::Handle> handles_;
134   IDList nodes_;
135 };
136 
TEST_F(QueueTest,Basic)137 TEST_F(QueueTest, Basic) {
138   buildSimpleTree();
139   dump();
140   EXPECT_EQ(nodes_, IDList({{0, 100}, {3, 25}, {5, 25}, {9, 100}, {7, 50}}));
141 
142   // Add another node, make sure we get the correct depth.
143   uint64_t depth;
144   addTransaction(11, {7, false, 15}, false, &depth);
145   EXPECT_EQ(depth, 3);
146 }
147 
TEST_F(QueueTest,RemoveLeaf)148 TEST_F(QueueTest, RemoveLeaf) {
149   buildSimpleTree();
150 
151   removeTransaction(3);
152   dump();
153 
154   EXPECT_EQ(nodes_, IDList({{0, 100}, {5, 33}, {9, 100}, {7, 66}}));
155 }
156 
TEST_F(QueueTest,RemoveParent)157 TEST_F(QueueTest, RemoveParent) {
158   buildSimpleTree();
159 
160   removeTransaction(5);
161   dump();
162 
163   EXPECT_EQ(nodes_, IDList({{0, 100}, {3, 25}, {7, 50}, {9, 25}}));
164 }
165 
TEST_F(QueueTest,RemoveParentWeights)166 TEST_F(QueueTest, RemoveParentWeights) {
167   // weight_ / totalChildWeight_ < 1
168   addTransaction(0, {kRootNodeId, false, 0});
169   addTransaction(3, {0, false, 255});
170   addTransaction(5, {0, false, 255});
171 
172   removeTransaction(0);
173   dump();
174 
175   EXPECT_EQ(nodes_, IDList({{3, 50}, {5, 50}}));
176 }
177 
TEST_F(QueueTest,NodeDepth)178 TEST_F(QueueTest, NodeDepth) {
179   uint64_t depth{33}; // initialize to some wrong value
180   addTransaction(0, {kRootNodeId, false, 15}, false, &depth);
181   EXPECT_EQ(depth, 1);
182 
183   addTransaction(3, {0, false, 3}, false, &depth);
184   EXPECT_EQ(depth, 2);
185 
186   addTransaction(5, {3, true, 7}, false, &depth);
187   EXPECT_EQ(depth, 3);
188 
189   addTransaction(9, {0, false, 3}, true, &depth);
190   EXPECT_EQ(depth, 2);
191   EXPECT_EQ(q_.numPendingEgress(), 3);
192   EXPECT_EQ(q_.numVirtualNodes(), 1);
193 
194   depth = 55; // some unlikely depth
195   addTransaction(9, {0, false, 31}, false, &depth);
196   EXPECT_EQ(depth, 2);
197   EXPECT_EQ(q_.numPendingEgress(), 4);
198   EXPECT_EQ(q_.numVirtualNodes(), 0);
199 
200   addTransaction(11, {0, true, 7}, false, &depth);
201   EXPECT_EQ(depth, 2);
202   EXPECT_EQ(q_.numPendingEgress(), 5);
203   EXPECT_EQ(q_.numVirtualNodes(), 0);
204 
205   addTransaction(13, {kRootNodeId, true, 23}, true, &depth);
206   EXPECT_EQ(depth, 1);
207   EXPECT_EQ(q_.numPendingEgress(), 5);
208   EXPECT_EQ(q_.numVirtualNodes(), 1);
209 
210   depth = 77; // some unlikely depth
211   addTransaction(13, {kRootNodeId, true, 33}, false, &depth);
212   EXPECT_EQ(depth, 1);
213   EXPECT_EQ(q_.numPendingEgress(), 6);
214   EXPECT_EQ(q_.numVirtualNodes(), 0);
215 }
216 
TEST_F(QueueTest,UpdateWeight)217 TEST_F(QueueTest, UpdateWeight) {
218   buildSimpleTree();
219 
220   uint64_t depth = 0;
221   updatePriority(5, {0, false, 7}, &depth);
222   dump();
223 
224   EXPECT_EQ(nodes_, IDList({{0, 100}, {3, 20}, {5, 40}, {9, 100}, {7, 40}}));
225   EXPECT_EQ(depth, 2);
226 }
227 
228 // Previously the code would allow duplicate entries in the priority tree under
229 // certain circumstances.
TEST_F(QueueTest,DuplicateID)230 TEST_F(QueueTest, DuplicateID) {
231   q_.addOrUpdatePriorityNode(0, {kRootNodeId, false, 15});
232   addTransaction(0, {kRootNodeId, true, 15});
233   q_.addOrUpdatePriorityNode(3, {0, false, 15});
234   addTransaction(5, {3, false, 15});
235   addTransaction(3, {5, false, 15});
236   removeTransaction(5);
237   auto stopFn = [] { return false; };
238 
239   dumpBFS(stopFn);
240   EXPECT_EQ(nodes_, IDList({{0, 100}, {3, 100}}));
241 }
242 
TEST_F(QueueTest,UpdateWeightNotEnqueued)243 TEST_F(QueueTest, UpdateWeightNotEnqueued) {
244   addTransaction(0, {kRootNodeId, false, 7});
245   addTransaction(3, {0, false, 7});
246 
247   signalEgress(0, false);
248   signalEgress(3, false);
249   uint64_t depth = 0;
250   updatePriority(0, {3, false, 7}, &depth);
251   dump();
252 
253   EXPECT_EQ(nodes_, IDList({{3, 100}, {0, 100}}));
254   EXPECT_EQ(depth, 2);
255 }
256 
TEST_F(QueueTest,UpdateWeightExcl)257 TEST_F(QueueTest, UpdateWeightExcl) {
258   buildSimpleTree();
259 
260   updatePriority(5, {0, true, 7});
261   dump();
262 
263   EXPECT_EQ(nodes_, IDList({{0, 100}, {5, 100}, {9, 40}, {3, 20}, {7, 40}}));
264   signalEgress(0, false);
265   nextEgress();
266   EXPECT_EQ(nodes_, IDList({{5, 100}}));
267 }
268 
TEST_F(QueueTest,UpdateWeightExclDequeued)269 TEST_F(QueueTest, UpdateWeightExclDequeued) {
270   buildSimpleTree();
271 
272   signalEgress(5, false);
273   updatePriority(5, {0, true, 7});
274   signalEgress(0, false);
275   nextEgress();
276 
277   EXPECT_EQ(nodes_, IDList({{9, 40}, {7, 40}, {3, 20}}));
278 }
279 
TEST_F(QueueTest,UpdateWeightUnknownParent)280 TEST_F(QueueTest, UpdateWeightUnknownParent) {
281   buildSimpleTree();
282 
283   uint64_t depth = 0;
284   updatePriority(5, {97, false, 15}, &depth);
285   dump();
286 
287   EXPECT_EQ(nodes_,
288             IDList({{0, 50}, {3, 33}, {7, 66}, {97, 50}, {5, 100}, {9, 100}}));
289   EXPECT_EQ(depth, 2);
290 
291   depth = 0;
292   updatePriority(9, {99, false, 15}, &depth);
293   dump();
294 
295   EXPECT_EQ(
296       nodes_,
297       IDList(
298           {{0, 33}, {3, 33}, {7, 66}, {97, 33}, {5, 100}, {99, 33}, {9, 100}}));
299   EXPECT_EQ(depth, 2);
300 }
301 
TEST_F(QueueTest,UpdateParentSibling)302 TEST_F(QueueTest, UpdateParentSibling) {
303   buildSimpleTree();
304 
305   updatePriority(5, {3, false, 3});
306   dump();
307 
308   EXPECT_EQ(nodes_, IDList({{0, 100}, {3, 33}, {5, 100}, {9, 100}, {7, 66}}));
309   signalEgress(0, false);
310   nextEgress();
311   EXPECT_EQ(nodes_, IDList({{7, 66}, {3, 33}}));
312 
313   // Clear 5's egress (so it is only in the tree because 9 has egress) and move
314   // it back.  Hit's a slightly different code path in reparent
315   signalEgress(5, false);
316   updatePriority(5, {0, false, 3});
317   dump();
318 
319   EXPECT_EQ(nodes_, IDList({{0, 100}, {3, 25}, {7, 50}, {5, 25}, {9, 100}}));
320 
321   nextEgress();
322   EXPECT_EQ(nodes_, IDList({{7, 50}, {3, 25}, {9, 25}}));
323 }
324 
TEST_F(QueueTest,UpdateParentSiblingExcl)325 TEST_F(QueueTest, UpdateParentSiblingExcl) {
326   buildSimpleTree();
327 
328   updatePriority(7, {5, true, 3});
329   dump();
330 
331   EXPECT_EQ(nodes_, IDList({{0, 100}, {3, 50}, {5, 50}, {7, 100}, {9, 100}}));
332   signalEgress(0, false);
333   signalEgress(3, false);
334   signalEgress(5, false);
335   nextEgress();
336   EXPECT_EQ(nodes_, IDList({{7, 100}}));
337 }
338 
TEST_F(QueueTest,UpdateParentAncestor)339 TEST_F(QueueTest, UpdateParentAncestor) {
340   buildSimpleTree();
341 
342   updatePriority(9, {kRootNodeId, false, 15});
343   dump();
344 
345   EXPECT_EQ(nodes_, IDList({{0, 50}, {3, 25}, {5, 25}, {7, 50}, {9, 50}}));
346   nextEgress();
347   EXPECT_EQ(nodes_, IDList({{0, 50}, {9, 50}}));
348 }
349 
TEST_F(QueueTest,UpdateParentAncestorExcl)350 TEST_F(QueueTest, UpdateParentAncestorExcl) {
351   buildSimpleTree();
352 
353   updatePriority(9, {kRootNodeId, true, 15});
354   dump();
355 
356   EXPECT_EQ(nodes_, IDList({{9, 100}, {0, 100}, {3, 25}, {5, 25}, {7, 50}}));
357   nextEgress();
358   EXPECT_EQ(nodes_, IDList({{9, 100}}));
359 }
360 
TEST_F(QueueTest,UpdateParentDescendant)361 TEST_F(QueueTest, UpdateParentDescendant) {
362   buildSimpleTree();
363 
364   updatePriority(0, {5, false, 7});
365   dump();
366 
367   EXPECT_EQ(nodes_, IDList({{5, 100}, {9, 50}, {0, 50}, {3, 33}, {7, 66}}));
368   nextEgress();
369   EXPECT_EQ(nodes_, IDList({{5, 100}}));
370   signalEgress(5, false);
371   nextEgress();
372   EXPECT_EQ(nodes_, IDList({{9, 50}, {0, 50}}));
373 }
374 
TEST_F(QueueTest,UpdateParentDescendantExcl)375 TEST_F(QueueTest, UpdateParentDescendantExcl) {
376   buildSimpleTree();
377 
378   updatePriority(0, {5, true, 7});
379   dump();
380 
381   EXPECT_EQ(nodes_, IDList({{5, 100}, {0, 100}, {3, 20}, {7, 40}, {9, 40}}));
382   nextEgress();
383   EXPECT_EQ(nodes_, IDList({{5, 100}}));
384   signalEgress(5, false);
385   signalEgress(0, false);
386   nextEgress();
387   EXPECT_EQ(nodes_, IDList({{7, 40}, {9, 40}, {3, 20}}));
388 }
389 
TEST_F(QueueTest,ExclusiveAdd)390 TEST_F(QueueTest, ExclusiveAdd) {
391   buildSimpleTree();
392 
393   addTransaction(11, {0, true, 100});
394 
395   dump();
396   EXPECT_EQ(nodes_,
397             IDList({{0, 100}, {11, 100}, {3, 25}, {5, 25}, {9, 100}, {7, 50}}));
398 }
399 
TEST_F(QueueTest,AddUnknown)400 TEST_F(QueueTest, AddUnknown) {
401   buildSimpleTree();
402 
403   addTransaction(11, {75, false, 15});
404 
405   dump();
406   EXPECT_EQ(
407       nodes_,
408       IDList(
409           {{0, 50}, {3, 25}, {5, 25}, {9, 100}, {7, 50}, {75, 50}, {11, 100}}));
410 
411   // Now let's add the missing parent node and check if it was
412   // relocated properly
413   addTransaction(75, {0, false, 7});
414 
415   dump();
416   EXPECT_EQ(nodes_,
417             IDList({{0, 100},
418                     {3, 16},
419                     {5, 16},
420                     {9, 100},
421                     {7, 33},
422                     {75, 33},
423                     {11, 100}}));
424 }
425 
TEST_F(QueueTest,AddMax)426 TEST_F(QueueTest, AddMax) {
427   addTransaction(0, {kRootNodeId, false, 255});
428 
429   nextEgress();
430   EXPECT_EQ(nodes_, IDList({{0, 100}}));
431 }
432 
TEST_F(QueueTest,Misc)433 TEST_F(QueueTest, Misc) {
434   buildSimpleTree();
435 
436   EXPECT_FALSE(q_.empty());
437   EXPECT_EQ(q_.numPendingEgress(), 5);
438   signalEgress(0, false);
439   EXPECT_EQ(q_.numPendingEgress(), 4);
440   EXPECT_FALSE(q_.empty());
441   removeTransaction(9);
442   removeTransaction(0);
443   dump();
444   EXPECT_EQ(nodes_, IDList({{3, 25}, {5, 25}, {7, 50}}));
445 }
446 
TEST_F(QueueTest,IterateBFS)447 TEST_F(QueueTest, IterateBFS) {
448   buildSimpleTree();
449 
450   auto stopFn = [this] { return nodes_.size() > 2; };
451 
452   dumpBFS(stopFn);
453   EXPECT_EQ(nodes_, IDList({{0, 100}, {3, 25}, {5, 25}, {7, 50}}));
454 }
455 
TEST_F(QueueTest,NextEgress)456 TEST_F(QueueTest, NextEgress) {
457   buildSimpleTree();
458 
459   nextEgress();
460   EXPECT_EQ(nodes_, IDList({{0, 100}}));
461 
462   addTransaction(11, {7, false, 15});
463   signalEgress(0, false);
464 
465   nextEgress();
466   EXPECT_EQ(nodes_, IDList({{7, 50}, {3, 25}, {5, 25}}));
467 
468   signalEgress(5, false);
469   nextEgress();
470   EXPECT_EQ(nodes_, IDList({{7, 50}, {3, 25}, {9, 25}}));
471   signalEgress(5, true);
472 
473   signalEgress(3, false);
474   nextEgress();
475   EXPECT_EQ(nodes_, IDList({{7, 66}, {5, 33}}));
476 
477   signalEgress(5, false);
478   nextEgress();
479   EXPECT_EQ(nodes_, IDList({{7, 66}, {9, 33}}));
480 
481   signalEgress(7, false);
482   nextEgress();
483   EXPECT_EQ(nodes_, IDList({{11, 66}, {9, 33}}));
484 
485   signalEgress(9, false);
486   nextEgress();
487   EXPECT_EQ(nodes_, IDList({{11, 100}}));
488 
489   signalEgress(3, true);
490   signalEgress(7, true);
491   signalEgress(9, true);
492   nextEgress();
493   EXPECT_EQ(nodes_, IDList({{7, 50}, {3, 25}, {9, 25}}));
494 }
495 
TEST_F(QueueTest,NextEgressExclusiveAdd)496 TEST_F(QueueTest, NextEgressExclusiveAdd) {
497   buildSimpleTree();
498 
499   // clear all egress
500   signalEgress(0, false);
501   signalEgress(3, false);
502   signalEgress(5, false);
503   signalEgress(7, false);
504   signalEgress(9, false);
505 
506   // Add a transaction with exclusive dependency, clear its egress
507   addTransaction(11, {0, true, 100});
508   signalEgress(11, false);
509 
510   // signal egress for a child that got moved via exclusive dep
511   signalEgress(3, true);
512   nextEgress();
513   EXPECT_EQ(nodes_, IDList({{3, 100}}));
514   EXPECT_EQ(q_.numPendingEgress(), 1);
515 }
516 
TEST_F(QueueTest,NextEgressExclusiveAddWithEgress)517 TEST_F(QueueTest, NextEgressExclusiveAddWithEgress) {
518   buildSimpleTree();
519 
520   // clear all egress, except 3
521   signalEgress(0, false);
522   signalEgress(5, false);
523   signalEgress(7, false);
524   signalEgress(9, false);
525 
526   // Add a transaction with exclusive dependency, clear its egress
527   addTransaction(11, {0, true, 100});
528   signalEgress(11, false);
529   nextEgress();
530   EXPECT_EQ(nodes_, IDList({{3, 100}}));
531   EXPECT_EQ(q_.numPendingEgress(), 1);
532 }
533 
TEST_F(QueueTest,UpdatePriorityReparentSubtree)534 TEST_F(QueueTest, UpdatePriorityReparentSubtree) {
535   buildSimpleTree();
536 
537   // clear all egress, except 9
538   signalEgress(0, false);
539   signalEgress(3, false);
540   signalEgress(5, false);
541   signalEgress(7, false);
542 
543   // Update priority of non-enqueued but in egress tree node
544   updatePriority(5, {0, false, 14}, nullptr);
545 
546   // update 9's weight and reparent
547   updatePriority(9, {3, false, 14}, nullptr);
548 
549   nextEgress();
550   EXPECT_EQ(nodes_, IDList({{9, 100}}));
551 }
552 
TEST_F(QueueTest,NextEgressRemoveParent)553 TEST_F(QueueTest, NextEgressRemoveParent) {
554   buildSimpleTree();
555 
556   // Clear egress for all except txn=9
557   signalEgress(0, false);
558   signalEgress(3, false);
559   signalEgress(5, false);
560   signalEgress(7, false);
561 
562   // Remove parent of 9 (5)
563   removeTransaction(5);
564   nextEgress();
565   EXPECT_EQ(nodes_, IDList({{9, 100}}));
566 
567   // signal egress for 9's new siblings to verify weights
568   signalEgress(3, true);
569   signalEgress(7, true);
570 
571   nextEgress();
572   EXPECT_EQ(nodes_, IDList({{7, 50}, {9, 25}, {3, 25}}));
573 }
574 
TEST_F(QueueTest,AddExclusiveDescendantEnqueued)575 TEST_F(QueueTest, AddExclusiveDescendantEnqueued) {
576   addTransaction(0, {kRootNodeId, false, 100});
577   addTransaction(3, {0, false, 100});
578   addTransaction(5, {3, false, 100});
579   signalEgress(0, false);
580   signalEgress(3, false);
581   // add a new exclusive child of 1.  1's child 3 is not enqueued but is in the
582   // the egress tree.
583   addTransaction(7, {0, true, 100});
584   nextEgress();
585   EXPECT_EQ(nodes_, IDList({{7, 100}}));
586 }
587 
TEST_F(QueueTest,NextEgressRemoveParentEnqueued)588 TEST_F(QueueTest, NextEgressRemoveParentEnqueued) {
589   addTransaction(0, {kRootNodeId, false, 100});
590   addTransaction(3, {0, false, 100});
591   addTransaction(5, {3, false, 100});
592   signalEgress(3, false);
593   // When 3's children (5) are added to 1, both are already in the egress tree
594   // and the signal does not need to propagate
595   removeTransaction(3);
596   signalEgress(0, false);
597   nextEgress();
598   EXPECT_EQ(nodes_, IDList({{5, 100}}));
599 }
600 
TEST_F(QueueTest,NextEgressRemoveParentEnqueuedIndirect)601 TEST_F(QueueTest, NextEgressRemoveParentEnqueuedIndirect) {
602   addTransaction(0, {kRootNodeId, false, 100});
603   addTransaction(3, {0, false, 100});
604   addTransaction(5, {3, false, 100});
605   addTransaction(7, {0, false, 100});
606   signalEgress(3, false);
607   signalEgress(0, false);
608   // When 3's children (5) are added to 1, both are already in the egress tree
609   // and the signal does not need to propagate
610   removeTransaction(3);
611   nextEgress();
612   EXPECT_EQ(nodes_, IDList({{7, 50}, {5, 50}}));
613 }
614 
TEST_F(QueueTest,ChromeTest)615 TEST_F(QueueTest, ChromeTest) {
616   // Tries to simulate Chrome's current behavior by performing pseudo-random
617   // add-exclusive, signal, clear and remove with 3 insertion points
618   // (hi,mid,low).  Note the test uses rand32() with a particular seed so the
619   // output is predictable.
620   HTTPCodec::StreamID pris[3] = {0, 3, 5};
621   addTransaction(0, {kRootNodeId, true, 99});
622   signalEgress(0, false);
623   addTransaction(3, {0, true, 99});
624   signalEgress(3, false);
625   addTransaction(5, {3, true, 99});
626   signalEgress(5, false);
627 
628   std::vector<HTTPCodec::StreamID> txns;
629   std::vector<HTTPCodec::StreamID> active;
630   std::vector<HTTPCodec::StreamID> inactive;
631   HTTPCodec::StreamID txn = 0;
632   uint64_t idx = 0;
633   HTTPCodec::StreamID nextId = 7;
634   auto gen = Random::create();
635   gen.seed(12345); // luggage combo
636   for (auto i = 4; i < 1000; i++) {
637     uint8_t action = rand32(4, gen);
638     if (action == 0) {
639       // add exclusive on pseudo-random priority anchor
640       uint8_t pri = rand32(3, gen);
641       HTTPCodec::StreamID dep = pris[pri];
642       txn = nextId;
643       nextId += 2;
644       VLOG(2) << "Adding txn=" << txn << " with dep=" << dep;
645       addTransaction(txn, {(uint32_t)dep, true, 99});
646       txns.push_back(txn);
647       active.push_back(txn);
648     } else if (action == 1 && !inactive.empty()) {
649       // signal an inactive txn
650       idx = rand32(inactive.size(), gen);
651       txn = inactive[idx];
652       VLOG(2) << "Activating txn=" << txn;
653       signalEgress(txn, true);
654       inactive.erase(inactive.begin() + idx);
655       active.push_back(txn);
656     } else if (action == 2 && !active.empty()) {
657       // clear an active transaction
658       idx = rand32(active.size(), gen);
659       txn = active[idx];
660       VLOG(2) << "Deactivating txn=" << txn;
661       signalEgress(txn, false);
662       active.erase(active.begin() + idx);
663       inactive.push_back(txn);
664     } else if (action == 3 && !txns.empty()) {
665       // remove a transaction
666       idx = rand32(txns.size(), gen);
667       txn = txns[idx];
668       VLOG(2) << "Removing txn=" << txn;
669       removeTransaction(txn);
670       txns.erase(txns.begin() + idx);
671       auto it = std::find(active.begin(), active.end(), txn);
672       if (it != active.end()) {
673         active.erase(it);
674       }
675       it = std::find(inactive.begin(), inactive.end(), txn);
676       if (it != inactive.end()) {
677         inactive.erase(it);
678       }
679     }
680     VLOG(2) << "Active nodes=" << q_.numPendingEgress();
681     if (!q_.empty()) {
682       nextEgress();
683       EXPECT_GT(nodes_.size(), 0);
684     }
685   }
686 }
687 
TEST_F(QueueTest,NextEgressSpdy)688 TEST_F(QueueTest, NextEgressSpdy) {
689   // 0 and 3 are vnodes representing pri 0 and 1
690   addTransaction(0, {kRootNodeId, false, 0}, true);
691   addTransaction(3, {0, false, 0}, true);
692 
693   // 7 and 9 are pri 0, 11 and 13 are pri 1
694   addTransaction(7, {0, false, 15});
695   addTransaction(9, {0, false, 15});
696   addTransaction(11, {3, false, 15});
697   addTransaction(13, {3, false, 15});
698 
699   nextEgress(true);
700   EXPECT_EQ(nodes_, IDList({{7, 50}, {9, 50}}));
701 
702   signalEgress(7, false);
703   nextEgress(true);
704   EXPECT_EQ(nodes_, IDList({{9, 100}}));
705 
706   signalEgress(9, false);
707   nextEgress(true);
708   EXPECT_EQ(nodes_, IDList({{11, 50}, {13, 50}}));
709 }
710 
TEST_F(QueueTest,AddOrUpdate)711 TEST_F(QueueTest, AddOrUpdate) {
712   q_.addOrUpdatePriorityNode(0, {kRootNodeId, false, 15});
713   q_.addOrUpdatePriorityNode(3, {kRootNodeId, false, 15});
714   dump();
715   EXPECT_EQ(nodes_, IDList({{0, 50}, {3, 50}}));
716   q_.addOrUpdatePriorityNode(0, {kRootNodeId, false, 3});
717   dump();
718   EXPECT_EQ(nodes_, IDList({{0, 20}, {3, 80}}));
719 }
720 
721 class DanglingQueueTestBase {
722  public:
DanglingQueueTestBase()723   DanglingQueueTestBase() {
724     // Just under two ticks
725     HTTP2PriorityQueue::setNodeLifetime(
726         std::chrono::milliseconds(2 * HHWheelTimer::DEFAULT_TICK_INTERVAL - 1));
727     EXPECT_CALL(timeoutManager_, scheduleTimeout(_, _))
728         .WillRepeatedly(Invoke(
729             [this](folly::AsyncTimeout* timeout, std::chrono::milliseconds) {
730               timeouts_.push_back(timeout);
731               return true;
732             }));
733   }
734 
735  protected:
expireNodes()736   void expireNodes() {
737     std::this_thread::sleep_for(
738         std::chrono::milliseconds(2 * HHWheelTimer::DEFAULT_TICK_INTERVAL));
739     // Node lifetime it just under two ticks, so firing twice expires all nodes
740     tick();
741     tick();
742   }
743 
tick()744   void tick() {
745     std::list<folly::AsyncTimeout*> timeouts;
746     std::swap(timeouts_, timeouts);
747     for (auto timeout : timeouts) {
748       timeout->timeoutExpired();
749     }
750   }
751 
752   std::list<folly::AsyncTimeout*> timeouts_;
753   testing::NiceMock<MockTimeoutManager> timeoutManager_;
754   folly::UndelayedDestruction<HHWheelTimer> timer_{&timeoutManager_};
755 };
756 
757 // Order declaration of the base classes for this fixture matters here: we want
758 // to pass the timer initialized as part of DanglingQueueTest into to QueueTest,
759 // so it must be initialized first.
760 class DanglingQueueTest
761     : public DanglingQueueTestBase
762     , public QueueTest {
763  public:
DanglingQueueTest()764   DanglingQueueTest() : DanglingQueueTestBase(), QueueTest(&timer_) {
765   }
766 };
767 
TEST_F(DanglingQueueTest,Basic)768 TEST_F(DanglingQueueTest, Basic) {
769   addTransaction(0, {kRootNodeId, false, 15});
770   removeTransaction(0);
771   dump();
772   EXPECT_EQ(nodes_, IDList({{0, 100}}));
773   expireNodes();
774   dump();
775   EXPECT_EQ(nodes_, IDList({}));
776 }
777 
TEST_F(DanglingQueueTest,Chain)778 TEST_F(DanglingQueueTest, Chain) {
779   addTransaction(0, {kRootNodeId, false, 15}, true);
780   addTransaction(3, {0, false, 15}, true);
781   addTransaction(5, {3, false, 15}, true);
782   dump();
783   EXPECT_EQ(nodes_, IDList({{0, 100}, {3, 100}, {5, 100}}));
784   expireNodes();
785   dump();
786   EXPECT_EQ(nodes_, IDList({{0, 100}, {3, 100}}));
787   expireNodes();
788   dump();
789   EXPECT_EQ(nodes_, IDList({{0, 100}}));
790   expireNodes();
791   dump();
792   EXPECT_EQ(nodes_, IDList({}));
793 }
794 
TEST_F(DanglingQueueTest,Drop)795 TEST_F(DanglingQueueTest, Drop) {
796   addTransaction(0, {kRootNodeId, false, 15}, true);
797   addTransaction(3, {0, false, 15}, true);
798   addTransaction(5, {0, false, 15}, true);
799   dump();
800   q_.dropPriorityNodes();
801   dump();
802   EXPECT_EQ(nodes_, IDList({}));
803 }
804 
TEST_F(DanglingQueueTest,ExpireParentOfMismatchedTwins)805 TEST_F(DanglingQueueTest, ExpireParentOfMismatchedTwins) {
806   addTransaction(0, {kRootNodeId, true, 219}, false);
807   addTransaction(3, {0, false, 146}, false);
808   addTransaction(5, {0, false, 146}, false);
809   signalEgress(3, false);
810   signalEgress(5, true);
811   removeTransaction(0);
812   dump();
813   tick();
814   expireNodes();
815   dump();
816   EXPECT_EQ(nodes_, IDList({{3, 50}, {5, 50}}));
817 }
818 
TEST_F(DanglingQueueTest,AddExpireAdd)819 TEST_F(DanglingQueueTest, AddExpireAdd) {
820   // Add a virtual node
821   addTransaction(0, {kRootNodeId, true, 219}, true);
822   // expire it
823   expireNodes();
824   dump();
825   EXPECT_TRUE(q_.empty());
826   // Add a real node with the same id
827   addTransaction(0, {kRootNodeId, true, 219}, false);
828   dump();
829   EXPECT_EQ(nodes_, IDList({{0, 100}}));
830 }
831 
832 class DummyTimeout : public HHWheelTimer::Callback {
timeoutExpired()833   void timeoutExpired() noexcept override {
834   }
835 };
836 
TEST_F(DanglingQueueTest,Refresh)837 TEST_F(DanglingQueueTest, Refresh) {
838   // Having a long running timeout prevents HHWheelTimer::Callback::setScheduled
839   // from checking the real time
840   DummyTimeout t;
841   timer_.scheduleTimeout(&t, std::chrono::seconds(300));
842   addTransaction(0, {kRootNodeId, false, 15});
843   addTransaction(3, {kRootNodeId, false, 15});
844   // 0 is now virtual
845   removeTransaction(0);
846   dump();
847   EXPECT_EQ(nodes_, IDList({{0, 50}, {3, 50}}));
848   tick();
849   // before 0 times out, change it's priority, should still be there
850   updatePriority(0, {kRootNodeId, false, 3});
851   dump();
852   EXPECT_EQ(nodes_, IDList({{0, 20}, {3, 80}}));
853 
854   tick();
855   dump();
856   EXPECT_EQ(nodes_, IDList({{0, 20}, {3, 80}}));
857   expireNodes();
858   dump();
859   EXPECT_EQ(nodes_, IDList({{3, 100}}));
860 }
861 
TEST_F(DanglingQueueTest,Max)862 TEST_F(DanglingQueueTest, Max) {
863   buildSimpleTree();
864   q_.setMaxVirtualNodes(3);
865   for (auto i = 1; i <= 9; i += 2) {
866     removeTransaction(i == 1 ? 0 : i);
867   }
868   dump();
869   EXPECT_EQ(nodes_, IDList({{0, 100}, {3, 50}, {5, 50}}));
870   // 0 expires first and it re-weights 3 and 5, which extends their lifetime
871   expireNodes();
872   dump();
873   EXPECT_EQ(nodes_, IDList({{3, 50}, {5, 50}}));
874   expireNodes();
875   dump();
876   EXPECT_EQ(nodes_, IDList());
877 }
878 
TEST_F(QueueTest,Rebuild)879 TEST_F(QueueTest, Rebuild) {
880   buildSimpleTree();
881   q_.rebuildTree();
882   dump();
883   EXPECT_EQ(nodes_, IDList({{3, 20}, {9, 20}, {5, 20}, {7, 20}, {0, 20}}));
884 }
885 
886 } // namespace proxygen
887