1 /** @file
2 
3   HTTP/2 Dependency Tree
4 
5   The original idea of Stream Priority Algorithm using Weighted Fair Queue (WFQ)
6   Scheduling is invented by Kazuho Oku (H2O project).
7 
8   @section license License
9 
10   Licensed to the Apache Software Foundation (ASF) under one
11   or more contributor license agreements.  See the NOTICE file
12   distributed with this work for additional information
13   regarding copyright ownership.  The ASF licenses this file
14   to you under the Apache License, Version 2.0 (the
15   "License"); you may not use this file except in compliance
16   with the License.  You may obtain a copy of the License at
17 
18       http://www.apache.org/licenses/LICENSE-2.0
19 
20   Unless required by applicable law or agreed to in writing, software
21   distributed under the License is distributed on an "AS IS" BASIS,
22   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23   See the License for the specific language governing permissions and
24   limitations under the License.
25  */
26 
27 #pragma once
28 
29 #include "tscore/List.h"
30 #include "tscore/Diags.h"
31 #include "tscore/PriorityQueue.h"
32 
33 #include "HTTP2.h"
34 
35 // TODO: K is a constant, 256 is temporal value.
36 const static uint32_t K                               = 256;
37 const static uint32_t HTTP2_DEPENDENCY_TREE_MAX_DEPTH = 256;
38 
39 namespace Http2DependencyTree
40 {
41 class Node
42 {
43 public:
t(t)44   explicit Node(void *t = nullptr) : t(t)
45   {
46     entry = new PriorityQueueEntry<Node *>(this);
47     queue = new PriorityQueue<Node *>();
48   }
49 
id(i)50   Node(uint32_t i, uint32_t w, uint32_t p, Node *n, void *t = nullptr) : id(i), weight(w), point(p), t(t), parent(n)
51   {
52     entry = new PriorityQueueEntry<Node *>(this);
53     queue = new PriorityQueue<Node *>();
54   }
55 
~Node()56   ~Node()
57   {
58     delete entry;
59     delete queue;
60 
61     // delete all child nodes
62     if (!children.empty()) {
63       Node *node = children.head;
64       Node *next = nullptr;
65       while (node) {
66         next = node->link.next;
67         children.remove(node);
68         delete node;
69         node = next;
70       }
71     }
72   }
73 
74   Node(const Node &) = delete;
75   Node &operator=(const Node &) = delete;
76 
77   LINK(Node, link);
78 
79   bool
80   operator<(const Node &n) const
81   {
82     return point < n.point;
83   }
84 
85   bool
86   operator>(const Node &n) const
87   {
88     return point > n.point;
89   }
90 
91   /**
92    * Added an explicit shadow flag.  The original logic
93    * was using an null Http2Stream frame to mark a shadow node
94    * but that would pull in Priority holder nodes too
95    */
96   bool
is_shadow()97   is_shadow() const
98   {
99     return shadow == true;
100   }
101 
102   bool active     = false;
103   bool queued     = false;
104   bool shadow     = false;
105   uint32_t id     = HTTP2_PRIORITY_DEFAULT_STREAM_DEPENDENCY;
106   uint32_t weight = HTTP2_PRIORITY_DEFAULT_WEIGHT;
107   uint32_t point  = 0;
108   void *t         = nullptr;
109   Node *parent    = nullptr;
110   DLL<Node> children;
111   PriorityQueueEntry<Node *> *entry;
112   PriorityQueue<Node *> *queue;
113 };
114 
115 template <typename T> class Tree
116 {
117 public:
Tree(uint32_t max_concurrent_streams)118   explicit Tree(uint32_t max_concurrent_streams) : _max_depth(MIN(max_concurrent_streams, HTTP2_DEPENDENCY_TREE_MAX_DEPTH))
119   {
120     _ancestors.resize(_max_ancestors);
121   }
~Tree()122   ~Tree() { delete _root; }
123   Node *find(uint32_t id, bool *is_max_leaf = nullptr);
124   Node *find_shadow(uint32_t id, bool *is_max_leaf = nullptr);
125   Node *add(uint32_t parent_id, uint32_t id, uint32_t weight, bool exclusive, T t, bool shadow = false);
126   Node *reprioritize(uint32_t id, uint32_t new_parent_id, bool exclusive);
127   Node *reprioritize(Node *node, uint32_t id, bool exclusive);
128   Node *top();
129   void remove(Node *node);
130   void activate(Node *node);
131   void deactivate(Node *node, uint32_t sent);
132   void update(Node *node, uint32_t sent);
133   bool in(Node *current, Node *node);
134   uint32_t size() const;
135   /*
136    * Dump the priority tree relationships in JSON form for debugging
137    */
138   void
dump_tree(std::ostream & output)139   dump_tree(std::ostream &output) const
140   {
141     _dump(_root, output);
142   }
143   void add_ancestor(Node *node);
144   uint32_t was_ancestor(uint32_t id) const;
145 
146 private:
147   void _dump(Node *node, std::ostream &output) const;
148   Node *_find(Node *node, uint32_t id, uint32_t depth = 1, bool *is_max_leaf = nullptr);
149   Node *_top(Node *node);
150   void _change_parent(Node *node, Node *new_parent, bool exclusive);
151   bool in_parent_chain(Node *maybe_parent, Node *target);
152 
153   Node *_root = new Node(this);
154   uint32_t _max_depth;
155   uint32_t _node_count = 0;
156   /*
157    * _ancestors in a circular buffer tracking parent relationships for
158    * recently completed nodes.  Without this new streams may not find their
159    * parents and be inserted at the root, violating the client's desired
160    * dependency relationship.  This addresses the issue identified in section
161    * 5.3.4 of the HTTP/2 spec
162    *
163    * "It is possible for a stream to become closed while prioritization
164    * information that creates a dependency on that stream is in transit.
165    * If a stream identified in a dependency has no associated priority
166    * information, then the dependent stream is instead assigned a default
167    * priority (Section 5.3.5).  This potentially creates suboptimal
168    * prioritization, since the stream could be given a priority that is
169    * different from what is intended.
170    * To avoid these problems, an endpoint SHOULD retain stream
171    * prioritization state for a period after streams become closed.  The
172    * longer state is retained, the lower the chance that streams are
173    * assigned incorrect or default priority values."
174    */
175   static const uint32_t _max_ancestors = 64;
176   uint32_t _ancestor_index             = 0;
177   std::vector<std::pair<uint32_t, uint32_t>> _ancestors;
178 };
179 
180 template <typename T>
181 void
_dump(Node * node,std::ostream & output)182 Tree<T>::_dump(Node *node, std::ostream &output) const
183 {
184   output << R"({ "id":")" << node->id << "/" << node->weight << "/" << node->point << "/" << ((node->t != nullptr) ? "1" : "0")
185          << "/" << ((node->active) ? "a" : "d") << "\",";
186   // Dump the children
187   output << " \"c\":[";
188   for (Node *n = node->children.head; n; n = n->link.next) {
189     _dump(n, output);
190     output << ",";
191   }
192   output << "] }";
193 }
194 
195 template <typename T>
196 Node *
_find(Node * node,uint32_t id,uint32_t depth,bool * is_max_leaf)197 Tree<T>::_find(Node *node, uint32_t id, uint32_t depth, bool *is_max_leaf)
198 {
199   if (node->id == id) {
200     if (is_max_leaf) {
201       *is_max_leaf = depth == _max_depth;
202     }
203     return node;
204   }
205 
206   if (node->children.empty() || depth > _max_depth) {
207     return nullptr;
208   }
209 
210   Node *result = nullptr;
211   for (Node *n = node->children.head; n; n = n->link.next) {
212     result = _find(n, id, ++depth, is_max_leaf);
213     if (result != nullptr) {
214       break;
215     }
216   }
217 
218   return result;
219 }
220 
221 template <typename T>
222 Node *
223 Tree<T>::find_shadow(uint32_t id, bool *is_max_leaf)
224 {
225   return _find(_root, id, 1, is_max_leaf);
226 }
227 
228 template <typename T>
229 Node *
230 Tree<T>::find(uint32_t id, bool *is_max_leaf)
231 {
232   Node *n = _find(_root, id, 1, is_max_leaf);
233   return n == nullptr ? nullptr : (n->is_shadow() ? nullptr : n);
234 }
235 
236 template <typename T>
237 void
238 Tree<T>::add_ancestor(Node *node)
239 {
240   if (node->parent != _root) {
241     _ancestors[_ancestor_index].first  = node->id;
242     _ancestors[_ancestor_index].second = node->parent->id;
243     _ancestor_index++;
244     if (_ancestor_index >= _max_ancestors) {
245       _ancestor_index = 0;
246     }
247   }
248 }
249 
250 template <typename T>
251 uint32_t
252 Tree<T>::was_ancestor(uint32_t pid) const
253 {
254   uint32_t i = (_ancestor_index == 0) ? _max_ancestors - 1 : _ancestor_index - 1;
255   while (i != _ancestor_index) {
256     if (_ancestors[i].first == pid) {
257       return _ancestors[i].second;
258     }
259     i = (i == 0) ? _max_ancestors - 1 : i - 1;
260   }
261   return 0;
262 }
263 
264 template <typename T>
265 Node *
266 Tree<T>::add(uint32_t parent_id, uint32_t id, uint32_t weight, bool exclusive, T t, bool shadow)
267 {
268   // Can we vivify a shadow node?
269   Node *node = find_shadow(id);
270   if (node != nullptr && node->is_shadow()) {
271     node->t      = t;
272     node->point  = id;
273     node->weight = weight;
274     node->shadow = false;
275     // Move the shadow node into the proper position in the tree
276     node = reprioritize(node, parent_id, exclusive);
277     return node;
278   }
279 
280   bool is_max_leaf = false;
281   Node *parent     = find_shadow(parent_id, &is_max_leaf); // Look for real and shadow nodes
282 
283   if (parent == nullptr) {
284     if (parent_id < id) { // See if we still have a history of the parent
285       uint32_t pid = parent_id;
286       do {
287         pid = was_ancestor(pid);
288         if (pid != 0) {
289           parent = find(pid);
290         }
291       } while (pid != 0 && parent == nullptr);
292       if (parent == nullptr) {
293         // Found no ancestor, just add to root at default weight
294         weight    = HTTP2_PRIORITY_DEFAULT_WEIGHT;
295         exclusive = false;
296         parent    = _root;
297       }
298     }
299     if (parent == nullptr || parent == _root) { // Create a shadow node
300       parent    = add(0, parent_id, HTTP2_PRIORITY_DEFAULT_WEIGHT, false, nullptr, true);
301       exclusive = false;
302     }
303   } else if (is_max_leaf) { // Chain too long, just add to root
304     parent    = _root;
305     exclusive = false;
306   }
307 
308   // Use stream id as initial point
309   node = new Node(id, weight, id, parent, t);
310 
311   if (exclusive) {
312     while (Node *child = parent->children.pop()) {
313       if (child->queued) {
314         parent->queue->erase(child->entry);
315         node->queue->push(child->entry);
316       }
317       node->children.push(child);
318       child->parent = node;
319     }
320   }
321 
322   parent->children.push(node);
323   if (!node->queue->empty()) {
324     ink_release_assert(!node->queued);
325     parent->queue->push(node->entry);
326     node->queued = true;
327   }
328   node->shadow = shadow;
329   ++_node_count;
330   return node;
331 }
332 
333 template <typename T>
334 bool
335 Tree<T>::in(Node *current, Node *node)
336 {
337   bool retval = false;
338   if (current == nullptr)
339     current = _root;
340   if (current->queue->in(node->entry)) {
341     return true;
342   } else {
343     Node *child = current->children.head;
344     while (child) {
345       if (in(child, node)) {
346         return true;
347       }
348       child = child->link.next;
349     }
350   }
351   return retval;
352 }
353 
354 template <typename T>
355 void
356 Tree<T>::remove(Node *node)
357 {
358   if (node == _root || node->active) {
359     return;
360   }
361 
362   // Make a note of node's ancestry
363   add_ancestor(node);
364 
365   Node *parent = node->parent;
366   parent->children.remove(node);
367   if (node->queued) {
368     parent->queue->erase(node->entry);
369   }
370 
371   // Push queue entries
372   while (!node->queue->empty()) {
373     parent->queue->push(node->queue->top());
374     node->queue->pop();
375   }
376 
377   // Push children
378   while (!node->children.empty()) {
379     Node *child = node->children.pop();
380     parent->children.push(child);
381     child->parent = parent;
382   }
383 
384   // delete the shadow parent
385   if (parent->is_shadow() && parent->children.empty() && parent->queue->empty()) {
386     remove(parent);
387   }
388 
389   // ink_release_assert(!this->in(nullptr, node));
390 
391   --_node_count;
392   delete node;
393 }
394 
395 template <typename T>
396 Node *
397 Tree<T>::reprioritize(uint32_t id, uint32_t new_parent_id, bool exclusive)
398 {
399   Node *node = find(id);
400   if (node == nullptr) {
401     return node;
402   }
403 
404   return reprioritize(node, new_parent_id, exclusive);
405 }
406 
407 template <typename T>
408 Node *
409 Tree<T>::reprioritize(Node *node, uint32_t new_parent_id, bool exclusive)
410 {
411   if (node == nullptr) {
412     return node;
413   }
414 
415   Node *old_parent = node->parent;
416   if (old_parent->id == new_parent_id) {
417     // Do nothing
418     return node;
419   }
420   // should not change the root node
421   ink_assert(node->parent);
422 
423   Node *new_parent = find(new_parent_id);
424   if (new_parent == nullptr) {
425     return node;
426   }
427   // If node is dependent on the new parent, must move the new parent first
428   if (new_parent_id != 0 && in_parent_chain(node, new_parent)) {
429     _change_parent(new_parent, old_parent, false);
430   }
431   _change_parent(node, new_parent, exclusive);
432 
433   // delete the shadow node
434   if (node->is_shadow() && node->children.empty() && node->queue->empty()) {
435     remove(node);
436     return nullptr;
437   }
438 
439   return node;
440 }
441 
442 template <typename T>
443 bool
444 Tree<T>::in_parent_chain(Node *maybe_parent, Node *target)
445 {
446   bool retval  = false;
447   Node *parent = target->parent;
448   while (parent != nullptr && !retval) {
449     retval = maybe_parent == parent;
450     parent = parent->parent;
451   }
452   return retval;
453 }
454 
455 // Change node's parent to new_parent
456 template <typename T>
457 void
458 Tree<T>::_change_parent(Node *node, Node *new_parent, bool exclusive)
459 {
460   ink_release_assert(node->parent != nullptr);
461   node->parent->children.remove(node);
462   if (node->queued) {
463     node->parent->queue->erase(node->entry);
464     node->queued = false;
465 
466     Node *current = node->parent;
467     while (current->queue->empty() && !current->active && current->parent != nullptr) {
468       current->parent->queue->erase(current->entry);
469       current->queued = false;
470       current         = current->parent;
471     }
472   }
473 
474   node->parent = nullptr;
475   if (exclusive) {
476     while (Node *child = new_parent->children.pop()) {
477       if (child->queued) {
478         child->parent->queue->erase(child->entry);
479         node->queue->push(child->entry);
480       }
481 
482       node->children.push(child);
483       ink_release_assert(child != node);
484       child->parent = node;
485     }
486   }
487 
488   new_parent->children.push(node);
489   ink_release_assert(node != new_parent);
490   node->parent = new_parent;
491 
492   if (node->active || !node->queue->empty()) {
493     Node *current = node;
494     while (current->parent != nullptr && !current->queued) {
495       current->parent->queue->push(current->entry);
496       current->queued = true;
497       current         = current->parent;
498     }
499   }
500 }
501 
502 template <typename T>
503 Node *
504 Tree<T>::_top(Node *node)
505 {
506   Node *child = node;
507 
508   while (child != nullptr) {
509     if (child->active) {
510       return child;
511     } else if (!child->queue->empty()) {
512       child = child->queue->top()->node;
513     } else {
514       return nullptr;
515     }
516   }
517 
518   return child;
519 }
520 
521 template <typename T>
522 Node *
523 Tree<T>::top()
524 {
525   return _top(_root);
526 }
527 
528 template <typename T>
529 void
530 Tree<T>::activate(Node *node)
531 {
532   node->active = true;
533 
534   while (node->parent != nullptr && !node->queued) {
535     node->parent->queue->push(node->entry);
536     node->queued = true;
537     node         = node->parent;
538   }
539 }
540 
541 template <typename T>
542 void
543 Tree<T>::deactivate(Node *node, uint32_t sent)
544 {
545   node->active = false;
546 
547   while (!node->active && node->queue->empty() && node->parent != nullptr) {
548     if (node->queued) {
549       node->parent->queue->erase(node->entry);
550       node->queued = false;
551     }
552 
553     node = node->parent;
554   }
555 
556   update(node, sent);
557 }
558 
559 template <typename T>
560 void
561 Tree<T>::update(Node *node, uint32_t sent)
562 {
563   while (node->parent != nullptr) {
564     node->point += sent * K / (node->weight + 1);
565 
566     if (node->queued) {
567       node->parent->queue->update(node->entry, true);
568     } else {
569       node->parent->queue->push(node->entry);
570       node->queued = true;
571     }
572 
573     node = node->parent;
574   }
575 }
576 
577 template <typename T>
578 uint32_t
579 Tree<T>::size() const
580 {
581   return _node_count;
582 }
583 } // namespace Http2DependencyTree
584