Roll src/third_party/WebKit eac3800:0237a66 (svn 202606:202607)
[chromium-blink-merge.git] / net / spdy / spdy_priority_tree.h
blob1652f77efe80bbab3fa70e039f279474bca9ba97
1 // Copyright 2014 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.
5 #ifndef NET_SPDY_SPDY_PRIORITY_TREE_H_
6 #define NET_SPDY_SPDY_PRIORITY_TREE_H_
8 #include <cmath>
9 #include <deque>
10 #include <map>
11 #include <queue>
12 #include <set>
13 #include <utility>
14 #include <vector>
16 #include "base/basictypes.h"
17 #include "base/containers/hash_tables.h"
18 #include "base/logging.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/stl_util.h"
22 namespace net {
24 // This data structure implements the HTTP/2 stream priority tree defined in
25 // section 5.3 of RFC 7540:
26 // http://tools.ietf.org/html/rfc7540#section-5.3
28 // Nodes can be added and removed, and dependencies between them defined.
29 // Nodes constitute a tree rooted at node ID 0: each node has a single parent
30 // node, and 0 or more child nodes. Individual nodes can be marked as ready to
31 // read/write, and then the whole structure can be queried to pick the next
32 // node to read/write out of those that are ready.
34 // The NodeId type must be a POD that supports comparison (most
35 // likely, it will be a number).
37 namespace test {
38 template <typename NodeId>
39 class SpdyPriorityTreePeer;
42 const int kRootNodeId = 0;
43 const int kDefaultWeight = 16;
44 const int kMinWeight = 1;
45 const int kMaxWeight = 256;
47 template <typename NodeId>
48 class SpdyPriorityTree {
49 public:
50 typedef std::pair<NodeId, float> PriorityNode;
51 typedef std::vector<PriorityNode> PriorityList;
53 SpdyPriorityTree();
55 // Orders in descending order of priority.
56 struct NodePriorityComparator {
57 bool operator ()(const std::pair<NodeId, float>& lhs,
58 const std::pair<NodeId, float>& rhs);
61 friend class test::SpdyPriorityTreePeer<NodeId>;
63 // Return the number of nodes currently in the tree.
64 int num_nodes() const;
66 // Return true if the tree contains a node with the given ID.
67 bool NodeExists(NodeId node_id) const;
69 // Add a new node with the given weight and parent. Non-exclusive nodes
70 // simply get added below the parent node. If exclusive = true, the node
71 // becomes the parent's sole child and the parent's previous children
72 // become the children of the new node.
73 // Returns true on success. Returns false if the node already exists
74 // in the tree, or if the parent node does not exist.
75 bool AddNode(NodeId node_id, NodeId parent_id, int weight, bool exclusive);
77 // Remove an existing node from the tree. Returns true on success, or
78 // false if the node doesn't exist.
79 bool RemoveNode(NodeId node_id);
81 // Get the weight of the given node.
82 int GetWeight(NodeId node_id) const;
84 // Get the parent of the given node. If the node doesn't exist, or is a root
85 // node (and thus has no parent), returns NodeId().
86 NodeId GetParent(NodeId node_id) const;
88 // Get the children of the given node. If the node doesn't exist, or has no
89 // child, returns empty vector.
90 std::vector<NodeId> GetChildren(NodeId node_id) const;
92 // Set the priority of the given node.
93 bool SetWeight(NodeId node_id, int weight);
95 // Set the parent of the given node. Returns true on success.
96 // Returns false and has no effect if the node and/or the parent doesn't
97 // exist. If the new parent is a descendant of the node (i.e. this would have
98 // created a cycle) then we rearrange the topology of the tree as described
99 // in section 5.3.3 of RFC 7540:
100 // https://tools.ietf.org/html/rfc7540#section-5.3.3
101 bool SetParent(NodeId node_id, NodeId parent_id, bool exclusive);
103 // Returns true if the node parent_id has child_id in its children.
104 bool HasChild(NodeId parent_id, NodeId child_id) const;
106 // Mark a node as blocked or unblocked. Return true on success, or false
107 // if unable to mark the specified node.
108 bool SetBlocked(NodeId node_id, bool blocked);
110 // Mark whether or not a node is ready to write; i.e. whether there is
111 // buffered data for the associated stream. Return true on success, or false
112 // if unable to mark the specified node.
113 bool SetReady(NodeId node_id, bool ready);
115 // Returns an ordered list of writeable nodes and their priorities.
116 // Priority is calculated as:
117 // parent's priority * (node's weight / sum of sibling weights)
118 PriorityList GetPriorityList();
120 private:
121 struct Node;
122 typedef std::vector<Node*> NodeVector;
123 typedef std::map<NodeId, Node*> NodeMap;
125 struct Node {
126 // ID for this node.
127 NodeId id;
128 // ID of parent node.
129 Node* parent = nullptr;
130 // Weights can range between 1 and 256 (inclusive).
131 int weight = kDefaultWeight;
132 // The total weight of this node's direct descendants.
133 int total_child_weights = 0;
134 // The total weight of direct descendants that are writeable
135 // (ready to write and not blocked). This value does not necessarily
136 // reflect the current state of the tree; instead, we lazily update it
137 // on calls to PropagateNodeState().
138 int total_writeable_child_weights = 0;
139 // Pointers to nodes for children, if any.
140 NodeVector children;
141 // Is the associated stream write-blocked?
142 bool blocked = false;
143 // Does the stream have data ready for writing?
144 bool ready = false;
145 // The fraction of resources to dedicate to this node.
146 float priority = 0;
149 static bool Remove(NodeVector* nodes, const Node* node);
151 // Update the value of total_writeable_child_weights for the given node
152 // to reflect the current state of the tree.
153 void PropagateNodeState(Node* node);
155 // Get the given node, or return nullptr if it doesn't exist.
156 const Node* FindNode(NodeId node_id) const;
157 Node* FindNode(NodeId node_id);
159 // Return true if all internal invariants hold (useful for unit tests).
160 // Unless there are bugs, this should always return true.
161 bool ValidateInvariantsForTests() const;
163 Node* root_node_; // pointee owned by all_nodes_
164 NodeMap all_nodes_; // maps from node IDs to Node objects
165 STLValueDeleter<NodeMap> all_nodes_deleter_;
167 DISALLOW_COPY_AND_ASSIGN(SpdyPriorityTree);
170 template <typename NodeId>
171 SpdyPriorityTree<NodeId>::SpdyPriorityTree()
172 : all_nodes_deleter_(&all_nodes_) {
173 root_node_ = new Node();
174 root_node_->id = kRootNodeId;
175 root_node_->weight = kDefaultWeight;
176 root_node_->parent = nullptr;
177 root_node_->priority = 1.0;
178 root_node_->ready = true;
179 all_nodes_[kRootNodeId] = root_node_;
182 template <typename NodeId>
183 bool SpdyPriorityTree<NodeId>::NodePriorityComparator::operator()(
184 const std::pair<NodeId, float>& lhs,
185 const std::pair<NodeId, float>& rhs) {
186 return lhs.second > rhs.second;
189 template <typename NodeId>
190 int SpdyPriorityTree<NodeId>::num_nodes() const {
191 return all_nodes_.size();
194 template <typename NodeId>
195 bool SpdyPriorityTree<NodeId>::NodeExists(NodeId node_id) const {
196 return ContainsKey(all_nodes_, node_id);
199 template <typename NodeId>
200 bool SpdyPriorityTree<NodeId>::AddNode(NodeId node_id,
201 NodeId parent_id,
202 int weight,
203 bool exclusive) {
204 if (NodeExists(node_id) || weight < kMinWeight || weight > kMaxWeight) {
205 return false;
207 Node* parent = FindNode(parent_id);
208 if (parent == nullptr) {
209 return false;
211 Node* new_node = new Node;
212 new_node->id = node_id;
213 new_node->weight = weight;
214 new_node->parent = parent;
215 all_nodes_[node_id] = new_node;
216 if (exclusive) {
217 // Move the parent's current children below the new node.
218 using std::swap;
219 swap(new_node->children, parent->children);
220 new_node->total_child_weights = parent->total_child_weights;
221 // Update each child's parent.
222 for (Node* child : new_node->children) {
223 child->parent = new_node;
225 // Clear parent's old child data.
226 DCHECK(parent->children.empty());
227 parent->total_child_weights = 0;
229 // Add new node to parent.
230 parent->children.push_back(new_node);
231 parent->total_child_weights += weight;
232 return true;
235 template <typename NodeId>
236 bool SpdyPriorityTree<NodeId>::RemoveNode(NodeId node_id) {
237 if (node_id == kRootNodeId) {
238 return false;
240 // Remove the node from table.
241 typename NodeMap::iterator it = all_nodes_.find(node_id);
242 if (it == all_nodes_.end()) {
243 return false;
245 scoped_ptr<Node> node(it->second);
246 all_nodes_.erase(it);
248 Node* parent = node->parent;
249 // Remove the node from parent's child list.
250 Remove(&parent->children, node.get());
251 parent->total_child_weights -= node->weight;
253 // Move the node's children to the parent's child list.
254 // Update each child's parent and weight.
255 for (Node* child : node->children) {
256 child->parent = parent;
257 parent->children.push_back(child);
258 // Divide the removed node's weight among its children, rounding to the
259 // nearest valid weight.
260 float float_weight = node->weight * static_cast<float>(child->weight) /
261 static_cast<float>(node->total_child_weights);
262 int new_weight = floor(float_weight + 0.5);
263 if (new_weight == 0) {
264 new_weight = 1;
266 child->weight = new_weight;
267 parent->total_child_weights += child->weight;
270 return true;
273 template <typename NodeId>
274 int SpdyPriorityTree<NodeId>::GetWeight(NodeId node_id) const {
275 const Node* node = FindNode(node_id);
276 return (node == nullptr) ? 0 : node->weight;
279 template <typename NodeId>
280 NodeId SpdyPriorityTree<NodeId>::GetParent(NodeId node_id) const {
281 const Node* node = FindNode(node_id);
282 // Root node has null parent.
283 return (node == nullptr || node->parent == nullptr) ? kRootNodeId
284 : node->parent->id;
287 template <typename NodeId>
288 std::vector<NodeId> SpdyPriorityTree<NodeId>::GetChildren(
289 NodeId node_id) const {
290 std::vector<NodeId> child_vec;
291 const Node* node = FindNode(node_id);
292 if (node != nullptr) {
293 child_vec.reserve(node->children.size());
294 for (Node* child : node->children) {
295 child_vec.push_back(child->id);
298 return child_vec;
301 template <typename NodeId>
302 bool SpdyPriorityTree<NodeId>::SetWeight(
303 NodeId node_id, int weight) {
304 if (!NodeExists(node_id)) {
305 return false;
307 if (weight < kMinWeight || weight > kMaxWeight) {
308 return false;
311 Node* node = all_nodes_[node_id];
312 if (node->parent != nullptr) {
313 node->parent->total_child_weights += (weight - node->weight);
315 node->weight = weight;
317 return true;
321 template <typename NodeId>
322 bool SpdyPriorityTree<NodeId>::SetParent(
323 NodeId node_id, NodeId parent_id, bool exclusive) {
324 if (node_id == kRootNodeId || node_id == parent_id) {
325 return false;
327 Node* node = FindNode(node_id);
328 Node* new_parent = FindNode(parent_id);
329 if (node == nullptr || new_parent == nullptr) {
330 return false;
333 // If the new parent is already the node's parent, we're done.
334 if (node->parent == new_parent) {
335 return true;
338 // Next, check to see if the new parent is currently a descendant
339 // of the node.
340 Node* last = new_parent->parent;
341 bool cycle_exists = false;
342 while (last != nullptr) {
343 if (last == node) {
344 cycle_exists = true;
345 break;
347 last = last->parent;
350 if (cycle_exists) {
351 // The new parent moves to the level of the current node.
352 SetParent(parent_id, node->parent->id, false);
355 // Remove node from old parent's child list.
356 Node* old_parent = node->parent;
357 Remove(&old_parent->children, node);
358 old_parent->total_child_weights -= node->weight;
360 if (exclusive) {
361 // Move the new parent's current children below the current node.
362 for (Node* child : new_parent->children) {
363 child->parent = node;
364 node->children.push_back(child);
366 node->total_child_weights += new_parent->total_child_weights;
367 // Clear new parent's old child data.
368 new_parent->children.clear();
369 new_parent->total_child_weights = 0;
372 // Make the change.
373 node->parent = new_parent;
374 new_parent->children.push_back(node);
375 new_parent->total_child_weights += node->weight;
376 return true;
379 template <typename NodeId>
380 bool SpdyPriorityTree<NodeId>::SetBlocked(NodeId node_id, bool blocked) {
381 if (!NodeExists(node_id)) {
382 return false;
385 Node* node = all_nodes_[node_id];
386 node->blocked = blocked;
387 return true;
390 template <typename NodeId>
391 bool SpdyPriorityTree<NodeId>::SetReady(NodeId node_id, bool ready) {
392 if (!NodeExists(node_id)) {
393 return false;
395 Node* node = all_nodes_[node_id];
396 node->ready = ready;
397 return true;
400 template <typename NodeId>
401 bool SpdyPriorityTree<NodeId>::Remove(NodeVector* nodes, const Node* node) {
402 for (typename NodeVector::iterator it = nodes->begin(); it != nodes->end();
403 ++it) {
404 if (*it == node) {
405 nodes->erase(it);
406 return true;
409 return false;
412 template <typename NodeId>
413 void SpdyPriorityTree<NodeId>::PropagateNodeState(Node* node) {
414 // Reset total_writeable_child_weights to its maximum value.
415 node->total_writeable_child_weights = node->total_child_weights;
416 for (Node* child : node->children) {
417 PropagateNodeState(child);
419 if (node->total_writeable_child_weights == 0 &&
420 (node->blocked || !node->ready)) {
421 // Tell the parent that this entire subtree is unwriteable.
422 node->parent->total_writeable_child_weights -= node->weight;
426 template <typename NodeId>
427 const typename SpdyPriorityTree<NodeId>::Node*
428 SpdyPriorityTree<NodeId>::FindNode(NodeId node_id) const {
429 typename NodeMap::const_iterator it = all_nodes_.find(node_id);
430 return (it == all_nodes_.end() ? nullptr : it->second);
433 template <typename NodeId>
434 typename SpdyPriorityTree<NodeId>::Node* SpdyPriorityTree<NodeId>::FindNode(
435 NodeId node_id) {
436 typename NodeMap::const_iterator it = all_nodes_.find(node_id);
437 return (it == all_nodes_.end() ? nullptr : it->second);
440 template <typename NodeId>
441 bool SpdyPriorityTree<NodeId>::HasChild(NodeId parent_id,
442 NodeId child_id) const {
443 const Node* parent = FindNode(parent_id);
444 if (parent == nullptr) {
445 return false;
447 auto found =
448 std::find_if(parent->children.begin(), parent->children.end(),
449 [child_id](Node* node) { return node->id == child_id; });
450 return found != parent->children.end();
453 template <typename NodeId>
454 std::vector<std::pair<NodeId, float> >
455 SpdyPriorityTree<NodeId>::GetPriorityList() {
456 PriorityList priority_list;
458 // Update total_writeable_child_weights to reflect the current
459 // state of the tree.
460 PropagateNodeState(root_node_);
462 std::deque<Node*> queue;
463 DCHECK(root_node_->priority == 1.0);
464 // Start by examining our top-level nodes.
465 for (Node* child : root_node_->children) {
466 queue.push_back(child);
468 while (!queue.empty()) {
469 Node* current_node = queue.front();
470 const Node* parent_node = current_node->parent;
471 if (current_node->blocked || !current_node->ready) {
472 if (current_node->total_writeable_child_weights > 0) {
473 // This node isn't writeable, but it has writeable children.
474 // Calculate the total fraction of resources we can allot
475 // to this subtree.
476 current_node->priority = parent_node->priority *
477 (static_cast<float>(current_node->weight) /
478 static_cast<float>(parent_node->total_writeable_child_weights));
479 // Examine the children.
480 for (Node* child : current_node->children) {
481 queue.push_back(child);
483 } else {
484 // There's nothing to see in this subtree.
485 current_node->priority = 0;
487 } else {
488 // This node is writeable; calculate its priority.
489 current_node->priority = parent_node->priority *
490 (static_cast<float>(current_node->weight) /
491 static_cast<float>(parent_node->total_writeable_child_weights));
492 // Add this node to the priority list.
493 priority_list.push_back(
494 PriorityNode(current_node->id, current_node->priority));
496 // Remove this node from the queue.
497 queue.pop_front();
500 // Sort the nodes in descending order of priority.
501 std::sort(priority_list.begin(), priority_list.end(),
502 NodePriorityComparator());
504 return priority_list;
507 template <typename NodeId>
508 bool SpdyPriorityTree<NodeId>::ValidateInvariantsForTests() const {
509 int total_nodes = 0;
510 int nodes_visited = 0;
511 // Iterate through all nodes in the map.
512 for (const auto& kv : all_nodes_) {
513 ++total_nodes;
514 ++nodes_visited;
515 const Node& node = *kv.second;
516 // All nodes except the root should have a parent, and should appear in
517 // the children of that parent.
518 if (node.id != kRootNodeId && !HasChild(node.parent->id, node.id)) {
519 DLOG(INFO) << "Parent node " << node.parent->id
520 << " does not exist, or does not list node " << node.id
521 << " as its child.";
522 return false;
525 if (!node.children.empty()) {
526 int total_child_weights = 0;
527 // Iterate through the node's children.
528 for (Node* child : node.children) {
529 ++nodes_visited;
530 // Each node in the list should exist and should have this node
531 // set as its parent.
532 if (!NodeExists(child->id) || node.id != GetParent(child->id)) {
533 DLOG(INFO) << "Child node " << child->id << " does not exist, "
534 << "or does not list " << node.id << " as its parent.";
535 return false;
537 total_child_weights += child->weight;
539 // Verify that total_child_weights is correct.
540 if (total_child_weights != node.total_child_weights) {
541 DLOG(INFO) << "Child weight totals do not agree. For node " << node.id
542 << " total_child_weights has value "
543 << node.total_child_weights
544 << ", expected " << total_child_weights;
545 return false;
550 // Make sure num_nodes reflects the total number of nodes the map contains.
551 if (total_nodes != num_nodes()) {
552 DLOG(INFO) << "Map contains incorrect number of nodes.";
553 return false;
555 // Validate the validation function; we should have visited each node twice
556 // (except for the root)
557 DCHECK(nodes_visited == 2*num_nodes() - 1);
558 return true;
561 } // namespace net
563 #endif // NET_SPDY_SPDY_PRIORITY_TREE_H_