Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 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. | |
| 4 | |
| 5 #ifndef NET_SPDY_SPDY_PRIORITY_TREE_H_ | |
| 6 #define NET_SPDY_SPDY_PRIORITY_TREE_H_ | |
| 7 | |
| 8 #include <cmath> | |
| 9 #include <list> | |
| 10 #include <map> | |
| 11 #include <queue> | |
| 12 #include <set> | |
| 13 | |
| 14 #include "base/basictypes.h" | |
| 15 #include "base/containers/hash_tables.h" | |
| 16 #include "base/logging.h" | |
| 17 #include "base/memory/scoped_ptr.h" | |
| 18 | |
| 19 namespace net { | |
| 20 | |
| 21 // This data structure implements the HTTP2 prioritization data structure | |
| 22 // defined in this document: http://go/http2-spec | |
|
Ryan Hamilton
2014/06/25 23:22:58
Consider a public URL here.
Johnny
2014/06/26 16:09:37
Done.
| |
| 23 // | |
| 24 // Nodes can be added and removed, and dependencies between them defined. Each | |
| 25 // node can have at most one parent and at most one child (forming a list), but | |
| 26 // there can be multiple lists, with each list root having its own priority. | |
| 27 // Individual nodes can also be marked as ready to read/write, and then the | |
| 28 // whole structure can be queried to pick the next node to read/write out of | |
| 29 // those ready. | |
| 30 // | |
| 31 // The NodeId type must be a POD that supports comparison (most | |
| 32 // likely, it will be a number). | |
| 33 | |
| 34 namespace test { | |
| 35 template <typename NodeId> | |
| 36 class SpdyPriorityTreePeer; | |
| 37 } | |
| 38 | |
| 39 const int kRootNodeId = 0; | |
| 40 const int kDefaultWeight = 16; | |
| 41 const int kMinWeight = 1; | |
| 42 const int kMaxWeight = 256; | |
| 43 | |
| 44 template <typename NodeId> | |
| 45 class SpdyPriorityTree { | |
| 46 typedef std::vector<std::pair<NodeId, float> > PriorityNodeList; | |
| 47 | |
| 48 public: | |
| 49 SpdyPriorityTree(); | |
| 50 ~SpdyPriorityTree(); | |
| 51 | |
| 52 typedef std::list<NodeId> List; | |
| 53 struct Node { | |
| 54 Node(); | |
| 55 ~Node(); | |
| 56 | |
| 57 NodeId id; | |
| 58 NodeId parent_id; | |
| 59 int weight; // Weights can range between 1 and 256 (inclusive). | |
| 60 // The total weight of this node's direct descendants. | |
| 61 int total_child_weights; | |
| 62 // The total weight of direct descendants that are writeable | |
| 63 // (ready to write and not blocked). This value does not necessarily | |
| 64 // reflect the current state of the tree; instead, we lazily update it | |
| 65 // on calls to PropagateNodeState(node.id). | |
| 66 int total_writeable_child_weights; | |
| 67 List* child_list; // node ID's of children, if any | |
| 68 bool blocked; // Is the associated stream write-blocked? | |
| 69 bool ready; // Does the stream have data ready for writing? | |
| 70 float priority; // The fraction of resources to dedicate to this node. | |
| 71 }; | |
| 72 | |
| 73 // Orders in descending order of priority. | |
| 74 struct NodePriorityComparator { | |
| 75 bool operator ()(const std::pair<NodeId, float>& lhs, | |
| 76 const std::pair<NodeId, float>& rhs); | |
| 77 }; | |
| 78 | |
| 79 friend class test::SpdyPriorityTreePeer<NodeId>; | |
| 80 | |
| 81 // Return the number of nodes currently in the tree. | |
| 82 int num_nodes() const; | |
|
Ryan Hamilton
2014/06/25 23:22:58
Should this be size_t?
Johnny
2014/06/26 16:09:37
I also think it ought to be, but am leaving as-is
| |
| 83 | |
| 84 // Return true if the tree contains a node with the given ID. | |
| 85 bool NodeExists(NodeId node_id) const; | |
| 86 | |
| 87 // Add a new node with the given weight and parent. Non-exclusive nodes | |
| 88 // simply get added below the parent node. If exclusive = true, the node | |
| 89 // becomes the parent's sole child and the parent's previous children | |
| 90 // become the children of the new node. | |
| 91 // Returns true on success. Returns false if the node already exists | |
| 92 // in the tree, or if the parent node does not exist. | |
| 93 bool AddNode(NodeId node_id, NodeId parent_id, int weight, bool exclusive); | |
| 94 | |
| 95 // Remove an existing node from the tree. Returns true on success, or | |
| 96 // false if the node doesn't exist. | |
| 97 bool RemoveNode(NodeId node_id); | |
| 98 | |
| 99 // Get the weight of the given node. | |
| 100 int GetWeight(NodeId node_id) const; | |
| 101 | |
| 102 // Get the parent of the given node. If the node doesn't exist, or is a root | |
| 103 // node (and thus has no parent), returns NodeId(). | |
| 104 NodeId GetParent(NodeId node_id) const; | |
| 105 | |
| 106 // Get the child list of the given node. If the node doesn't exist, or has no | |
| 107 // child, returns NULL. | |
| 108 std::list<NodeId>* GetChildren(NodeId node_id) const; | |
| 109 | |
| 110 // Set the priority of the given node. | |
| 111 bool SetWeight(NodeId node_id, int weight); | |
| 112 | |
| 113 // Set the parent of the given node. Returns true on success. | |
| 114 // Returns false and has no effect if the node and/or the parent doesn't | |
| 115 // exist. If the new parent is a descendant of the node (i.e. this would have | |
| 116 // created a cycle) then we rearrange the topology of the tree as described | |
| 117 // in the HTTP2 spec. | |
| 118 bool SetParent(NodeId node_id, NodeId parent_id, bool exclusive); | |
| 119 | |
| 120 // Returns true if the node parent_id has child_id in its child_list. | |
| 121 bool HasChild(NodeId parent_id, NodeId child_id) const; | |
| 122 | |
| 123 // Mark a node as blocked or unblocked. Return true on success, or false | |
| 124 // if unable to mark the specified node. | |
| 125 bool SetBlocked(NodeId node_id, bool blocked); | |
| 126 | |
| 127 // Mark whether or not a node is ready to write; i.e. whether there is | |
| 128 // buffered data for the associated stream. Return true on success, or false | |
| 129 // if unable to mark the specified node. | |
| 130 bool SetReady(NodeId node_id, bool ready); | |
| 131 | |
| 132 // Return true if all internal invariants hold (useful for unit tests). | |
| 133 // Unless there are bugs, this should always return true. | |
| 134 bool ValidateInvariantsForTests() const; | |
| 135 | |
| 136 // Get the given node, or return NULL if it doesn't exist. | |
| 137 const Node* FindNode(NodeId node_id) const; | |
| 138 | |
| 139 // Returns an ordered list of writeable nodes and their priorities. | |
| 140 // Priority is calculated as: | |
| 141 // parent's priority * (node's weight / sum of sibling weights) | |
| 142 PriorityNodeList GetPriorityList(); | |
| 143 | |
| 144 protected: | |
| 145 // Update the value of total_writeable_child_weights for the given node | |
| 146 // to reflect the current state of the tree. | |
| 147 void PropagateNodeState(NodeId node); | |
| 148 | |
| 149 private: | |
| 150 typedef base::hash_map<NodeId, Node> NodeMap; | |
| 151 | |
| 152 NodeMap all_nodes_; // maps from node IDs to Node objects | |
| 153 | |
| 154 DISALLOW_COPY_AND_ASSIGN(SpdyPriorityTree); | |
| 155 }; | |
| 156 | |
| 157 template <typename NodeId> | |
| 158 SpdyPriorityTree<NodeId>::SpdyPriorityTree() { | |
| 159 Node* root_node = &all_nodes_[kRootNodeId]; | |
| 160 root_node->id = kRootNodeId; | |
| 161 root_node->weight = kDefaultWeight; | |
| 162 root_node->parent_id = static_cast<NodeId>(kRootNodeId); | |
| 163 root_node->child_list = new std::list<NodeId>; | |
| 164 root_node->priority = 1.0; | |
| 165 root_node->ready = true; | |
| 166 } | |
| 167 | |
| 168 template <typename NodeId> | |
| 169 SpdyPriorityTree<NodeId>::~SpdyPriorityTree() {} | |
| 170 | |
| 171 template <typename NodeId> | |
| 172 SpdyPriorityTree<NodeId>::Node::Node() : | |
| 173 parent_id(kRootNodeId), | |
| 174 weight(kDefaultWeight), | |
| 175 total_child_weights(0), | |
| 176 total_writeable_child_weights(0), | |
| 177 child_list(), | |
| 178 blocked(false), | |
| 179 ready(false), | |
| 180 priority(0) { | |
| 181 } | |
| 182 | |
| 183 template <typename NodeId> | |
| 184 SpdyPriorityTree<NodeId>::Node::~Node() { | |
| 185 delete child_list; | |
| 186 } | |
| 187 | |
| 188 template <typename NodeId> | |
| 189 bool SpdyPriorityTree<NodeId>::NodePriorityComparator::operator ()( | |
| 190 const std::pair<NodeId, float>& lhs, | |
| 191 const std::pair<NodeId, float>& rhs) { | |
| 192 return lhs.second > rhs.second; | |
| 193 } | |
| 194 | |
| 195 template <typename NodeId> | |
| 196 int SpdyPriorityTree<NodeId>::num_nodes() const { | |
| 197 return all_nodes_.size(); | |
| 198 } | |
| 199 | |
| 200 template <typename NodeId> | |
| 201 bool SpdyPriorityTree<NodeId>::NodeExists(NodeId node_id) const { | |
| 202 return all_nodes_.count(node_id) != 0; | |
| 203 } | |
| 204 | |
| 205 template <typename NodeId> | |
| 206 bool SpdyPriorityTree<NodeId>::AddNode(NodeId node_id, | |
| 207 NodeId parent_id, | |
| 208 int weight, | |
| 209 bool exclusive) { | |
| 210 if (NodeExists(node_id) || !NodeExists(parent_id)) { | |
| 211 return false; | |
| 212 } | |
| 213 if (weight < kMinWeight || weight > kMaxWeight) { | |
| 214 return false; | |
| 215 } | |
| 216 Node* parent = &all_nodes_[parent_id]; | |
| 217 Node* new_node = &all_nodes_[node_id]; | |
| 218 new_node->id = node_id; | |
| 219 new_node->weight = weight; | |
| 220 new_node->parent_id = parent_id; | |
| 221 if (exclusive) { | |
| 222 // Move the parent's current children below the new node. | |
| 223 new_node->child_list = parent->child_list; | |
| 224 new_node->total_child_weights = parent->total_child_weights; | |
| 225 // Update each child's parent_id. | |
| 226 for (typename List::iterator it = new_node->child_list->begin(); | |
| 227 it != new_node->child_list->end(); ++it) { | |
| 228 Node* child = &all_nodes_[*it]; | |
| 229 child->parent_id = node_id; | |
| 230 } | |
| 231 // Clear parent's old child data. | |
| 232 parent->child_list = new std::list<NodeId>; | |
| 233 parent->total_child_weights = 0; | |
| 234 } else { | |
| 235 new_node->child_list = new std::list<NodeId>; | |
| 236 } | |
| 237 // Add new node to parent. | |
| 238 parent->child_list->push_back(node_id); | |
| 239 parent->total_child_weights += weight; | |
| 240 return true; | |
| 241 } | |
| 242 | |
| 243 template <typename NodeId> | |
| 244 bool SpdyPriorityTree<NodeId>::RemoveNode(NodeId node_id) { | |
| 245 if (node_id == static_cast<NodeId>(kRootNodeId) || !NodeExists(node_id)) { | |
| 246 return false; | |
| 247 } | |
| 248 const Node& node = all_nodes_[node_id]; | |
| 249 | |
| 250 DCHECK(NodeExists(node.parent_id)); | |
| 251 Node* parent = &all_nodes_[node.parent_id]; | |
| 252 // Remove the node id from parent's child list. | |
| 253 parent->child_list->remove(node_id); | |
| 254 parent->total_child_weights -= node.weight; | |
| 255 | |
| 256 // Move the node's children to the parent's child list. | |
| 257 if (node.child_list != NULL) { | |
| 258 // Update each child's parent_id and weight. | |
| 259 for (typename List::iterator it = node.child_list->begin(); | |
| 260 it != node.child_list->end(); ++it) { | |
| 261 Node* child = &all_nodes_[*it]; | |
| 262 child->parent_id = node.parent_id; | |
| 263 // Divide the removed node's weight among its children, rounding to the | |
| 264 // nearest valid weight. | |
| 265 float float_weight = node.weight * static_cast<float>(child->weight) / | |
| 266 static_cast<float>(node.total_child_weights); | |
| 267 int new_weight = std::floor(float_weight + 0.5); | |
| 268 if (new_weight == 0) { | |
| 269 new_weight = 1; | |
| 270 } | |
| 271 child->weight = new_weight; | |
| 272 parent->total_child_weights += child->weight; | |
| 273 } | |
| 274 parent->child_list->splice(parent->child_list->end(), *node.child_list); | |
| 275 } | |
| 276 | |
| 277 // Delete the node. | |
| 278 all_nodes_.erase(node_id); | |
| 279 return true; | |
| 280 } | |
| 281 | |
| 282 template <typename NodeId> | |
| 283 int SpdyPriorityTree<NodeId>::GetWeight(NodeId node_id) const { | |
| 284 const Node* node = FindNode(node_id); | |
| 285 if (node != NULL) { | |
| 286 return node->weight; | |
| 287 } | |
| 288 return 0; | |
| 289 } | |
| 290 | |
| 291 template <typename NodeId> | |
| 292 NodeId SpdyPriorityTree<NodeId>::GetParent(NodeId node_id) const { | |
| 293 const Node* node = FindNode(node_id); | |
| 294 if (node != NULL && node->id != static_cast<NodeId>(kRootNodeId)) { | |
| 295 return node->parent_id; | |
| 296 } | |
| 297 return static_cast<NodeId>(kRootNodeId); | |
| 298 } | |
| 299 | |
| 300 template <typename NodeId> | |
| 301 std::list<NodeId>* SpdyPriorityTree<NodeId>::GetChildren(NodeId node_id) const { | |
| 302 const Node* node = FindNode(node_id); | |
| 303 if (node != NULL) { | |
| 304 return node->child_list; | |
| 305 } | |
| 306 return NULL; | |
| 307 } | |
| 308 | |
| 309 template <typename NodeId> | |
| 310 bool SpdyPriorityTree<NodeId>::SetWeight( | |
| 311 NodeId node_id, int weight) { | |
| 312 if (!NodeExists(node_id)) { | |
| 313 return false; | |
| 314 } | |
| 315 if (weight < kMinWeight || weight > kMaxWeight) { | |
| 316 return false; | |
| 317 } | |
| 318 | |
| 319 Node* node = &all_nodes_[node_id]; | |
| 320 Node* parent = &all_nodes_[node->parent_id]; | |
| 321 | |
| 322 parent->total_child_weights += (weight - node->weight); | |
| 323 node->weight = weight; | |
| 324 | |
| 325 return true; | |
| 326 } | |
| 327 | |
| 328 | |
| 329 template <typename NodeId> | |
| 330 bool SpdyPriorityTree<NodeId>::SetParent( | |
| 331 NodeId node_id, NodeId parent_id, bool exclusive) { | |
| 332 if (!NodeExists(node_id) || !NodeExists(parent_id)) { | |
| 333 return false; | |
| 334 } | |
| 335 if (node_id == parent_id) return false; | |
| 336 | |
| 337 Node* node = &all_nodes_[node_id]; | |
| 338 Node* new_parent = &all_nodes_[parent_id]; | |
| 339 // If the new parent is already the node's parent, we're done. | |
| 340 if (node->parent_id == parent_id) { | |
| 341 return true; | |
| 342 } | |
| 343 | |
| 344 // Next, check to see if the new parent is currently a descendant | |
| 345 // of the node. | |
| 346 Node* last = new_parent; | |
| 347 NodeId last_id = parent_id; | |
| 348 bool cycle_exists = false; | |
| 349 while (last->parent_id != static_cast<NodeId>(kRootNodeId)) { | |
| 350 if (last->parent_id == node_id) { | |
| 351 cycle_exists = true; | |
| 352 break; | |
| 353 } | |
| 354 last_id = last->parent_id; | |
| 355 DCHECK(NodeExists(last_id)); | |
| 356 last = &all_nodes_[last_id]; | |
| 357 } | |
| 358 | |
| 359 if (cycle_exists) { | |
| 360 // The new parent moves to the level of the current node. | |
| 361 SetParent(parent_id, node->parent_id, false); | |
| 362 } | |
| 363 | |
| 364 // Remove node from old parent's child list. | |
| 365 const NodeId old_parent_id = node->parent_id; | |
| 366 DCHECK(NodeExists(old_parent_id)); | |
| 367 Node* old_parent = &all_nodes_[old_parent_id]; | |
| 368 old_parent->child_list->remove(node_id); | |
| 369 old_parent->total_child_weights -= node->weight; | |
| 370 | |
| 371 // Make the change. | |
| 372 node->parent_id = parent_id; | |
| 373 new_parent->child_list->push_back(node_id); | |
| 374 new_parent->total_child_weights += node->weight; | |
| 375 return true; | |
| 376 } | |
| 377 | |
| 378 template <typename NodeId> | |
| 379 bool SpdyPriorityTree<NodeId>::SetBlocked(NodeId node_id, bool blocked) { | |
| 380 if (!NodeExists(node_id)) { | |
| 381 return false; | |
| 382 } | |
| 383 | |
| 384 Node* node = &all_nodes_[node_id]; | |
| 385 node->blocked = blocked; | |
| 386 return true; | |
| 387 } | |
| 388 | |
| 389 template <typename NodeId> | |
| 390 bool SpdyPriorityTree<NodeId>::SetReady(NodeId node_id, bool ready) { | |
| 391 if (!NodeExists(node_id)) { | |
| 392 return false; | |
| 393 } | |
| 394 Node* node = &all_nodes_[node_id]; | |
| 395 node->ready = ready; | |
| 396 return true; | |
| 397 } | |
| 398 | |
| 399 template <typename NodeId> | |
| 400 void SpdyPriorityTree<NodeId>::PropagateNodeState(NodeId node_id) { | |
| 401 // Reset total_writeable_child_weights to its maximum value. | |
| 402 Node* node = &all_nodes_[node_id]; | |
| 403 node->total_writeable_child_weights = node->total_child_weights; | |
| 404 for (typename List::iterator it = node->child_list->begin(); | |
| 405 it != node->child_list->end(); ++it) { | |
| 406 PropagateNodeState(*it); | |
| 407 } | |
| 408 if (node->total_writeable_child_weights == 0 && | |
| 409 (node->blocked || !node->ready)) { | |
| 410 // Tell the parent that this entire subtree is unwriteable. | |
| 411 Node* parent = &all_nodes_[node->parent_id]; | |
| 412 parent->total_writeable_child_weights -= node->weight; | |
| 413 } | |
| 414 } | |
| 415 | |
| 416 template <typename NodeId> | |
| 417 const typename SpdyPriorityTree<NodeId>::Node* | |
| 418 SpdyPriorityTree<NodeId>::FindNode(NodeId node_id) const { | |
| 419 typename NodeMap::const_iterator iter = all_nodes_.find(node_id); | |
| 420 if (iter == all_nodes_.end()) { | |
| 421 return NULL; | |
| 422 } | |
| 423 return &iter->second; | |
| 424 } | |
| 425 | |
| 426 template <typename NodeId> | |
| 427 bool SpdyPriorityTree<NodeId>::HasChild(NodeId parent_id, | |
| 428 NodeId child_id) const { | |
| 429 const Node* parent = FindNode(parent_id); | |
| 430 return parent->child_list->end() != | |
| 431 std::find(parent->child_list->begin(), | |
| 432 parent->child_list->end(), | |
| 433 child_id); | |
| 434 } | |
| 435 | |
| 436 template <typename NodeId> | |
| 437 std::vector<std::pair<NodeId, float> > | |
| 438 SpdyPriorityTree<NodeId>::GetPriorityList() { | |
| 439 typedef std::pair<NodeId, float> PriorityNode; | |
| 440 typedef std::vector<PriorityNode> PriorityList; | |
| 441 PriorityList priority_list; | |
| 442 | |
| 443 // Update total_writeable_child_weights to reflect the current | |
| 444 // state of the tree. | |
| 445 PropagateNodeState(kRootNodeId); | |
| 446 | |
| 447 List queue; | |
| 448 const Node* root_node = FindNode(kRootNodeId); | |
| 449 DCHECK(root_node->priority == 1.0); | |
| 450 // Start by examining our top-level nodes. | |
| 451 for (typename List::iterator it = root_node->child_list->begin(); | |
| 452 it != root_node->child_list->end(); ++it) { | |
| 453 queue.push_back(*it); | |
| 454 } | |
| 455 while (!queue.empty()) { | |
| 456 NodeId current_node_id = queue.front(); | |
| 457 Node* current_node = &all_nodes_[current_node_id]; | |
| 458 const Node* parent_node = FindNode(current_node->parent_id); | |
| 459 if (current_node->blocked || !current_node->ready) { | |
| 460 if (current_node->total_writeable_child_weights > 0) { | |
| 461 // This node isn't writeable, but it has writeable children. | |
| 462 // Calculate the total fraction of resources we can allot | |
| 463 // to this subtree. | |
| 464 current_node->priority = parent_node->priority * | |
| 465 (static_cast<float>(current_node->weight) / | |
| 466 static_cast<float>(parent_node->total_writeable_child_weights)); | |
| 467 // Examine the children. | |
| 468 for (typename List::iterator it = current_node->child_list->begin(); | |
| 469 it != current_node->child_list->end(); ++it) { | |
| 470 queue.push_back(*it); | |
| 471 } | |
| 472 } else { | |
| 473 // There's nothing to see in this subtree. | |
| 474 current_node->priority = 0; | |
| 475 } | |
| 476 } else { | |
| 477 // This node is writeable; calculate its priority. | |
| 478 current_node->priority = parent_node->priority * | |
| 479 (static_cast<float>(current_node->weight) / | |
| 480 static_cast<float>(parent_node->total_writeable_child_weights)); | |
| 481 // Add this node to the priority list. | |
| 482 priority_list.push_back(PriorityNode(current_node_id, | |
| 483 current_node->priority)); | |
| 484 } | |
| 485 // Remove this node from the queue. | |
| 486 queue.pop_front(); | |
| 487 } | |
| 488 | |
| 489 // Sort the nodes in descending order of priority. | |
| 490 std::sort(priority_list.begin(), priority_list.end(), | |
| 491 NodePriorityComparator()); | |
| 492 | |
| 493 return priority_list; | |
| 494 } | |
| 495 | |
| 496 template <typename NodeId> | |
| 497 bool SpdyPriorityTree<NodeId>::ValidateInvariantsForTests() const { | |
| 498 int total_nodes = 0; | |
| 499 int nodes_visited = 0; | |
| 500 // Iterate through all nodes in the map. | |
| 501 for (typename NodeMap::const_iterator iter = all_nodes_.begin(); | |
| 502 iter != all_nodes_.end(); ++iter) { | |
| 503 ++total_nodes; | |
| 504 ++nodes_visited; | |
| 505 const Node& node = iter->second; | |
| 506 // All nodes except the root should have a parent, and should appear in | |
| 507 // the child_list of that parent. | |
| 508 if (node.id != static_cast<NodeId>(kRootNodeId) && | |
| 509 (!NodeExists(node.parent_id) || | |
| 510 !HasChild(node.parent_id, node.id))) { | |
| 511 DLOG(INFO) << "Parent node " << node.parent_id | |
| 512 << " does not exist, or does not list node " << node.id | |
| 513 << " as its child."; | |
| 514 return false; | |
| 515 } | |
| 516 | |
| 517 if (!node.child_list->empty()) { | |
| 518 int total_child_weights = 0; | |
| 519 // Iterate through the node's children. | |
| 520 for (typename List::iterator it = node.child_list->begin(); | |
| 521 it != node.child_list->end(); ++it) { | |
| 522 ++nodes_visited; | |
| 523 // Each node in the list should exist and should have this node | |
| 524 // set as its parent. | |
| 525 if (!NodeExists(*it) || node.id != GetParent(*it)) { | |
| 526 DLOG(INFO) << "Child node " << *it << " does not exist, " | |
| 527 << "or does not list " << node.id << " as its parent."; | |
| 528 return false; | |
| 529 } | |
| 530 const Node* child = FindNode(*it); | |
| 531 total_child_weights += child->weight; | |
| 532 } | |
| 533 // Verify that total_child_weights is correct. | |
| 534 if (total_child_weights != node.total_child_weights) { | |
| 535 DLOG(INFO) << "Child weight totals do not agree. For node " << node.id | |
| 536 << " total_child_weights has value " | |
| 537 << node.total_child_weights | |
| 538 << ", expected " << total_child_weights; | |
| 539 return false; | |
| 540 } | |
| 541 } | |
| 542 } | |
| 543 | |
| 544 // Make sure num_nodes reflects the total number of nodes the map contains. | |
| 545 if (total_nodes != num_nodes()) { | |
| 546 DLOG(INFO) << "Map contains incorrect number of nodes."; | |
| 547 return false; | |
| 548 } | |
| 549 // Validate the validation function; we should have visited each node twice | |
| 550 // (except for the root) | |
| 551 DCHECK(nodes_visited == 2*num_nodes() - 1); | |
| 552 return true; | |
| 553 } | |
| 554 | |
| 555 } // namespace net | |
| 556 | |
| 557 #endif // NET_SPDY_SPDY_PRIORITY_TREE_H_ | |
| OLD | NEW |