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