Home | History | Annotate | Download | only in spdy
      1 // Copyright (c) 2013 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_FOREST_H_
      6 #define NET_SPDY_SPDY_PRIORITY_FOREST_H_
      7 
      8 #include <map>
      9 #include <set>
     10 #include <vector>
     11 
     12 #include "base/basictypes.h"
     13 #include "base/containers/hash_tables.h"
     14 #include "base/logging.h"
     15 #include "base/memory/scoped_ptr.h"
     16 #include "base/rand_util.h"
     17 
     18 namespace net {
     19 
     20 // This data structure implements the SPDY prioriziation data structures
     21 // defined in this document: http://go/spdy4-prioritization
     22 //
     23 // Nodes can be added and removed, and dependencies between them defined.  Each
     24 // node can have at most one parent and at most one child (forming a list), but
     25 // there can be multiple lists, with each list root having its own priority.
     26 // Individual nodes can also be marked as ready to read/write, and then the
     27 // whole structure can be queried to pick the next node to read/write out of
     28 // those ready.
     29 //
     30 // The NodeId and Priority types must be POD that support comparison (most
     31 // likely, they will be numbers).
     32 template <typename NodeId, typename Priority>
     33 class SpdyPriorityForest {
     34  public:
     35   SpdyPriorityForest();
     36   ~SpdyPriorityForest();
     37 
     38   // Return the number of nodes currently in the forest.
     39   int num_nodes() const;
     40 
     41   // Return true if the forest contains a node with the given ID.
     42   bool NodeExists(NodeId node_id) const;
     43 
     44   // Add a new root node to the forest, with the given priority.  Returns true
     45   // on success, or false if the node_id already exists within the forest.
     46   bool AddRootNode(NodeId node_id, Priority priority);
     47 
     48   // Add a new node to the forest, with the given parent.  Returns true on
     49   // success.  Returns false and has no effect if the new node already exists,
     50   // or if the parent doesn't exist, or if the parent already has a child.
     51   bool AddNonRootNode(NodeId node_id, NodeId parent_id, bool unordered);
     52 
     53   // Remove an existing node from the forest.  Returns true on success, or
     54   // false if the node doesn't exist.
     55   bool RemoveNode(NodeId node_id);
     56 
     57   // Get the priority of the given node.  If the node doesn't exist, or is not
     58   // a root node (and thus has no priority), returns Priority().
     59   Priority GetPriority(NodeId node_id) const;
     60 
     61   // Get the parent of the given node.  If the node doesn't exist, or is a root
     62   // node (and thus has no parent), returns NodeId().
     63   NodeId GetParent(NodeId node_id) const;
     64 
     65   // Determine if the given node is unordered with respect to its parent.  If
     66   // the node doesn't exist, or is a root node (and thus has no parent),
     67   // returns false.
     68   bool IsNodeUnordered(NodeId node_id) const;
     69 
     70   // Get the child of the given node.  If the node doesn't exist, or has no
     71   // child, returns NodeId().
     72   NodeId GetChild(NodeId node_id) const;
     73 
     74   // Set the priority of the given node.  If the node was not already a root
     75   // node, this makes it a root node.  Returns true on success, or false if the
     76   // node doesn't exist.
     77   bool SetPriority(NodeId node_id, Priority priority);
     78 
     79   // Set the parent of the given node.  If the node was a root node, this makes
     80   // it no longer a root.  Returns true on success.  Returns false and has no
     81   // effect if (1) the node and/or the parent doesn't exist, (2) the new parent
     82   // already has a different child than the node, or (3) if the new parent is a
     83   // descendant of the node (so this would have created a cycle).
     84   bool SetParent(NodeId node_id, NodeId parent_id, bool unordered);
     85 
     86   // Check if a node is marked as ready to read.  Returns false if the node
     87   // doesn't exist.
     88   bool IsMarkedReadyToRead(NodeId node_id) const;
     89   // Mark the node as ready or not ready to read.  Returns true on success, or
     90   // false if the node doesn't exist.
     91   bool MarkReadyToRead(NodeId node_id);
     92   bool MarkNoLongerReadyToRead(NodeId node_id);
     93   // Return the ID of the next node that we should read, or return NodeId() if
     94   // no node in the forest is ready to read.
     95   NodeId NextNodeToRead();
     96 
     97   // Check if a node is marked as ready to write.  Returns false if the node
     98   // doesn't exist.
     99   bool IsMarkedReadyToWrite(NodeId node_id) const;
    100   // Mark the node as ready or not ready to write.  Returns true on success, or
    101   // false if the node doesn't exist.
    102   bool MarkReadyToWrite(NodeId node_id);
    103   bool MarkNoLongerReadyToWrite(NodeId node_id);
    104   // Return the ID of the next node that we should write, or return NodeId() if
    105   // no node in the forest is ready to write.
    106   NodeId NextNodeToWrite();
    107 
    108   // Return true if all internal invariants hold (useful for unit tests).
    109   // Unless there are bugs, this should always return true.
    110   bool ValidateInvariantsForTests() const;
    111 
    112  private:
    113   enum NodeType { ROOT_NODE, NONROOT_ORDERED, NONROOT_UNORDERED };
    114   struct Node {
    115     Node() : type(ROOT_NODE), flags(0), child() {
    116       depends_on.priority = Priority();
    117     }
    118     NodeType type;
    119     unsigned int flags;  // bitfield of flags
    120     union {
    121       Priority priority;  // used for root nodes
    122       NodeId parent_id;  // used for non-root nodes
    123     } depends_on;
    124     NodeId child;  // node ID of child (or NodeId() for no child)
    125   };
    126 
    127   typedef base::hash_map<NodeId, Node> NodeMap;
    128 
    129   // Constants for the Node.flags bitset:
    130   // kReadToRead: set for nodes that are ready for reading
    131   static const unsigned int kReadyToRead = (1 << 0);
    132   // kReadToWrite: set for nodes that are ready for writing
    133   static const unsigned int kReadyToWrite = (1 << 1);
    134 
    135   // Common code for IsMarkedReadyToRead and IsMarkedReadyToWrite.
    136   bool IsMarked(NodeId node_id, unsigned int flag) const;
    137   // Common code for MarkReadyToRead and MarkReadyToWrite.
    138   bool Mark(NodeId node_id, unsigned int flag);
    139   // Common code for MarkNoLongerReadyToRead and MarkNoLongerReadyToWrite.
    140   bool Unmark(NodeId node_id, unsigned int flag);
    141   // Common code for NextNodeToRead and NextNodeToWrite;
    142   NodeId FirstMarkedNode(unsigned int flag);
    143   // Get the given node, or return NULL if it doesn't exist.
    144   const Node* FindNode(NodeId node_id) const;
    145 
    146   NodeMap all_nodes_;  // maps from node IDs to Node objects
    147 
    148   DISALLOW_COPY_AND_ASSIGN(SpdyPriorityForest);
    149 };
    150 
    151 template <typename NodeId, typename Priority>
    152 SpdyPriorityForest<NodeId, Priority>::SpdyPriorityForest() {}
    153 
    154 template <typename NodeId, typename Priority>
    155 SpdyPriorityForest<NodeId, Priority>::~SpdyPriorityForest() {}
    156 
    157 template <typename NodeId, typename Priority>
    158 int SpdyPriorityForest<NodeId, Priority>::num_nodes() const {
    159   return all_nodes_.size();
    160 }
    161 
    162 template <typename NodeId, typename Priority>
    163 bool SpdyPriorityForest<NodeId, Priority>::NodeExists(NodeId node_id) const {
    164   return all_nodes_.count(node_id) != 0;
    165 }
    166 
    167 template <typename NodeId, typename Priority>
    168 bool SpdyPriorityForest<NodeId, Priority>::AddRootNode(
    169     NodeId node_id, Priority priority) {
    170   if (NodeExists(node_id)) {
    171     return false;
    172   }
    173   Node* new_node = &all_nodes_[node_id];
    174   new_node->type = ROOT_NODE;
    175   new_node->depends_on.priority = priority;
    176   return true;
    177 }
    178 
    179 template <typename NodeId, typename Priority>
    180 bool SpdyPriorityForest<NodeId, Priority>::AddNonRootNode(
    181     NodeId node_id, NodeId parent_id, bool unordered) {
    182   if (NodeExists(node_id) || !NodeExists(parent_id)) {
    183     return false;
    184   }
    185 
    186   Node* parent = &all_nodes_[parent_id];
    187   if (parent->child != NodeId()) {
    188     return false;
    189   }
    190 
    191   Node* new_node = &all_nodes_[node_id];
    192   new_node->type = (unordered ? NONROOT_UNORDERED : NONROOT_ORDERED);
    193   new_node->depends_on.parent_id = parent_id;
    194   parent->child = node_id;
    195   return true;
    196 }
    197 
    198 template <typename NodeId, typename Priority>
    199 bool SpdyPriorityForest<NodeId, Priority>::RemoveNode(NodeId node_id) {
    200   if (!NodeExists(node_id)) {
    201     return false;
    202   }
    203   const Node& node = all_nodes_[node_id];
    204 
    205   // If the node to be removed is not a root node, we need to change its
    206   // parent's child ID.
    207   if (node.type != ROOT_NODE) {
    208     DCHECK(NodeExists(node.depends_on.parent_id));
    209     Node* parent = &all_nodes_[node.depends_on.parent_id];
    210     DCHECK_EQ(node_id, parent->child);
    211     parent->child = node.child;
    212   }
    213 
    214   // If the node has a child, we need to change the child's priority or parent.
    215   if (node.child != NodeId()) {
    216     DCHECK(NodeExists(node.child));
    217     Node* child = &all_nodes_[node.child];
    218     DCHECK_NE(ROOT_NODE, child->type);
    219     DCHECK_EQ(node_id, child->depends_on.parent_id);
    220     // Make the child's new depends_on be the node's depends_on (whether that
    221     // be a priority or a parent node ID).
    222     child->depends_on = node.depends_on;
    223     // If the removed node was a root, its child is now a root.  Otherwise, the
    224     // child will be be unordered if and only if it was already unordered and
    225     // the removed not is also not ordered.
    226     if (node.type == ROOT_NODE) {
    227       child->type = ROOT_NODE;
    228     } else if (node.type == NONROOT_ORDERED) {
    229       child->type = NONROOT_ORDERED;
    230     }
    231   }
    232 
    233   // Delete the node.
    234   all_nodes_.erase(node_id);
    235   return true;
    236 }
    237 
    238 template <typename NodeId, typename Priority>
    239 Priority SpdyPriorityForest<NodeId, Priority>::GetPriority(
    240     NodeId node_id) const {
    241   const Node* node = FindNode(node_id);
    242   if (node != NULL && node->type == ROOT_NODE) {
    243     return node->depends_on.priority;
    244   } else {
    245     return Priority();
    246   }
    247 }
    248 
    249 template <typename NodeId, typename Priority>
    250 NodeId SpdyPriorityForest<NodeId, Priority>::GetParent(NodeId node_id) const {
    251   const Node* node = FindNode(node_id);
    252   if (node != NULL && node->type != ROOT_NODE) {
    253     return node->depends_on.parent_id;
    254   } else {
    255     return NodeId();
    256   }
    257 }
    258 
    259 template <typename NodeId, typename Priority>
    260 bool SpdyPriorityForest<NodeId, Priority>::IsNodeUnordered(
    261     NodeId node_id) const {
    262   const Node* node = FindNode(node_id);
    263   return node != NULL && node->type == NONROOT_UNORDERED;
    264 }
    265 
    266 template <typename NodeId, typename Priority>
    267 NodeId SpdyPriorityForest<NodeId, Priority>::GetChild(NodeId node_id) const {
    268   const Node* node = FindNode(node_id);
    269   if (node != NULL) {
    270     return node->child;
    271   } else {
    272     return NodeId();
    273   }
    274 }
    275 
    276 template <typename NodeId, typename Priority>
    277 bool SpdyPriorityForest<NodeId, Priority>::SetPriority(
    278     NodeId node_id, Priority priority) {
    279   if (!NodeExists(node_id)) {
    280     return false;
    281   }
    282 
    283   Node* node = &all_nodes_[node_id];
    284   // If this is not already a root node, we need to make it be a root node.
    285   if (node->type != ROOT_NODE) {
    286     DCHECK(NodeExists(node->depends_on.parent_id));
    287     Node* parent = &all_nodes_[node->depends_on.parent_id];
    288     parent->child = NodeId();
    289     node->type = ROOT_NODE;
    290   }
    291 
    292   node->depends_on.priority = priority;
    293   return true;
    294 }
    295 
    296 template <typename NodeId, typename Priority>
    297 bool SpdyPriorityForest<NodeId, Priority>::SetParent(
    298     NodeId node_id, NodeId parent_id, bool unordered) {
    299   if (!NodeExists(node_id) || !NodeExists(parent_id)) {
    300     return false;
    301   }
    302 
    303   Node* node = &all_nodes_[node_id];
    304   Node* new_parent = &all_nodes_[parent_id];
    305   // If the new parent is already the node's parent, all we have to do is
    306   // update the node type and we're done.
    307   if (new_parent->child == node_id) {
    308     node->type = (unordered ? NONROOT_UNORDERED : NONROOT_ORDERED);
    309     return true;
    310   }
    311   // Otherwise, if the new parent already has a child, we fail.
    312   if (new_parent->child != NodeId()) {
    313     return false;
    314   }
    315 
    316   // Next, make sure we won't create a cycle.
    317   if (node_id == parent_id) return false;
    318   Node* last = node;
    319   NodeId last_id = node_id;
    320   while (last->child != NodeId()) {
    321     if (last->child == parent_id) return false;
    322     last_id = last->child;
    323     DCHECK(NodeExists(last_id));
    324     last = &all_nodes_[last_id];
    325   }
    326 
    327   // If the node is not a root, we need clear its old parent's child field
    328   // (unless the old parent is the same as the new parent).
    329   if (node->type != ROOT_NODE) {
    330     const NodeId old_parent_id = node->depends_on.parent_id;
    331     DCHECK(NodeExists(old_parent_id));
    332     DCHECK(old_parent_id != parent_id);
    333     Node* old_parent = &all_nodes_[old_parent_id];
    334     DCHECK_EQ(node_id, old_parent->child);
    335     old_parent->child = NodeId();
    336   }
    337 
    338   // Make the change.
    339   node->type = (unordered ? NONROOT_UNORDERED : NONROOT_ORDERED);
    340   node->depends_on.parent_id = parent_id;
    341   new_parent->child = node_id;
    342   return true;
    343 }
    344 
    345 template <typename NodeId, typename Priority>
    346 bool SpdyPriorityForest<NodeId, Priority>::IsMarkedReadyToRead(
    347     NodeId node_id) const {
    348   return IsMarked(node_id, kReadyToRead);
    349 }
    350 
    351 template <typename NodeId, typename Priority>
    352 bool SpdyPriorityForest<NodeId, Priority>::MarkReadyToRead(NodeId node_id) {
    353   return Mark(node_id, kReadyToRead);
    354 }
    355 
    356 template <typename NodeId, typename Priority>
    357 bool SpdyPriorityForest<NodeId, Priority>::MarkNoLongerReadyToRead(
    358     NodeId node_id) {
    359   return Unmark(node_id, kReadyToRead);
    360 }
    361 
    362 template <typename NodeId, typename Priority>
    363 NodeId SpdyPriorityForest<NodeId, Priority>::NextNodeToRead() {
    364   return FirstMarkedNode(kReadyToRead);
    365 }
    366 
    367 template <typename NodeId, typename Priority>
    368 bool SpdyPriorityForest<NodeId, Priority>::IsMarkedReadyToWrite(
    369     NodeId node_id) const {
    370   return IsMarked(node_id, kReadyToWrite);
    371 }
    372 
    373 template <typename NodeId, typename Priority>
    374 bool SpdyPriorityForest<NodeId, Priority>::MarkReadyToWrite(NodeId node_id) {
    375   return Mark(node_id, kReadyToWrite);
    376 }
    377 
    378 template <typename NodeId, typename Priority>
    379 bool SpdyPriorityForest<NodeId, Priority>::MarkNoLongerReadyToWrite(
    380     NodeId node_id) {
    381   return Unmark(node_id, kReadyToWrite);
    382 }
    383 
    384 template <typename NodeId, typename Priority>
    385 NodeId SpdyPriorityForest<NodeId, Priority>::NextNodeToWrite() {
    386   return FirstMarkedNode(kReadyToWrite);
    387 }
    388 
    389 template <typename NodeId, typename Priority>
    390 bool SpdyPriorityForest<NodeId, Priority>::IsMarked(
    391     NodeId node_id, unsigned int flag) const {
    392   const Node* node = FindNode(node_id);
    393   return node != NULL && (node->flags & flag) != 0;
    394 }
    395 
    396 template <typename NodeId, typename Priority>
    397 bool SpdyPriorityForest<NodeId, Priority>::Mark(
    398     NodeId node_id, unsigned int flag) {
    399   if (!NodeExists(node_id)) {
    400     return false;
    401   }
    402   all_nodes_[node_id].flags |= flag;
    403   return true;
    404 }
    405 
    406 template <typename NodeId, typename Priority>
    407 bool SpdyPriorityForest<NodeId, Priority>::Unmark(
    408     NodeId node_id, unsigned int flag) {
    409   if (!NodeExists(node_id)) {
    410     return false;
    411   }
    412   all_nodes_[node_id].flags &= ~flag;
    413   return true;
    414 }
    415 
    416 template <typename NodeId, typename Priority>
    417 NodeId SpdyPriorityForest<NodeId, Priority>::FirstMarkedNode(
    418     unsigned int flag) {
    419   // TODO(mdsteele): This is an *incredibly* stupid brute force solution.
    420 
    421   // Get all root nodes that have at least one marked child.
    422   uint64 total_weight = 0;
    423   std::map<uint64, NodeId> roots;  // maps cumulative weight to root node ID
    424   for (typename NodeMap::const_iterator iter = all_nodes_.begin();
    425        iter != all_nodes_.end(); ++iter) {
    426     const NodeId root_id = iter->first;
    427     const Node& root = iter->second;
    428     if (root.type == ROOT_NODE) {
    429       // See if there is at least one marked node in this root's chain.
    430       for (const Node* node = &root; ; node = &all_nodes_[node->child]) {
    431         if ((node->flags & flag) != 0) {
    432           total_weight += static_cast<uint64>(root.depends_on.priority);
    433           roots[total_weight] = root_id;
    434           break;
    435         }
    436         if (node->child == NodeId()) {
    437           break;
    438         }
    439         DCHECK(NodeExists(node->child));
    440       }
    441     }
    442   }
    443 
    444   // If there are no ready nodes, then return NodeId().
    445   if (total_weight == 0) {
    446     DCHECK(roots.empty());
    447     return NodeId();
    448   } else {
    449     DCHECK(!roots.empty());
    450   }
    451 
    452   // Randomly select a tree to use.
    453   typename std::map<uint64, NodeId>::const_iterator root_iter =
    454       roots.upper_bound(base::RandGenerator(total_weight));
    455   DCHECK(root_iter != roots.end());
    456   const NodeId root_id = root_iter->second;
    457 
    458   // Find the first node in the chain that is ready.
    459   NodeId node_id = root_id;
    460   while (true) {
    461     DCHECK(NodeExists(node_id));
    462     Node* node = &all_nodes_[node_id];
    463     if ((node->flags & flag) != 0) {
    464       // There might be more nodes that are ready and that are linked to this
    465       // one in an unordered chain.  Find all of them, then pick one randomly.
    466       std::vector<NodeId> group;
    467       group.push_back(node_id);
    468       for (Node* next = node; next->child != NodeId();) {
    469         DCHECK(NodeExists(next->child));
    470         Node *child = &all_nodes_[next->child];
    471         DCHECK_NE(ROOT_NODE, child->type);
    472         if (child->type != NONROOT_UNORDERED) {
    473           break;
    474         }
    475         if ((child->flags & flag) != 0) {
    476           group.push_back(next->child);
    477         }
    478         next = child;
    479       }
    480       return group[base::RandGenerator(group.size())];
    481     }
    482     node_id = node->child;
    483   }
    484 }
    485 
    486 template <typename NodeId, typename Priority>
    487 const typename SpdyPriorityForest<NodeId, Priority>::Node*
    488 SpdyPriorityForest<NodeId, Priority>::FindNode(NodeId node_id) const {
    489   typename NodeMap::const_iterator iter = all_nodes_.find(node_id);
    490   if (iter == all_nodes_.end()) {
    491     return NULL;
    492   }
    493   return &iter->second;
    494 }
    495 
    496 template <typename NodeId, typename Priority>
    497 bool SpdyPriorityForest<NodeId, Priority>::ValidateInvariantsForTests() const {
    498   for (typename NodeMap::const_iterator iter = all_nodes_.begin();
    499        iter != all_nodes_.end(); ++iter) {
    500     const NodeId node_id = iter->first;
    501     const Node& node = iter->second;
    502     if (node.type != ROOT_NODE &&
    503         (!NodeExists(node.depends_on.parent_id) ||
    504          GetChild(node.depends_on.parent_id) != node_id)) {
    505       return false;
    506     }
    507     if (node.child != NodeId()) {
    508       if (!NodeExists(node.child) || node_id != GetParent(node.child)) {
    509         return false;
    510       }
    511     }
    512 
    513     NodeId child_id = node.child;
    514     int count = 0;
    515     while (child_id != NodeId()) {
    516       if (count > num_nodes() || node_id == child_id) {
    517         return false;
    518       }
    519       child_id = GetChild(child_id);
    520       ++count;
    521     }
    522   }
    523   return true;
    524 }
    525 
    526 }  // namespace net
    527 
    528 #endif  // NET_SPDY_SPDY_PRIORITY_FOREST_H_
    529