Pass CreateDirectory errors up to IndexedDB.
[chromium-blink-merge.git] / net / spdy / spdy_priority_forest.h
blob4f00f566caff8dbb01ad9c009e663797963a5839
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.
5 #ifndef NET_SPDY_SPDY_PRIORITY_FOREST_H_
6 #define NET_SPDY_SPDY_PRIORITY_FOREST_H_
8 #include <map>
9 #include <set>
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"
17 namespace net {
19 // This data structure implements the SPDY prioriziation data structures
20 // defined in this document: http://go/spdy4-prioritization
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.
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();
37 // Return the number of nodes currently in the forest.
38 int num_nodes() const;
40 // Return true if the forest contains a node with the given ID.
41 bool NodeExists(NodeId node_id) const;
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);
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);
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);
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;
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;
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;
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;
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);
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);
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();
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();
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;
111 private:
112 enum NodeType { ROOT_NODE, NONROOT_ORDERED, NONROOT_UNORDERED };
113 struct Node {
114 Node() : type(ROOT_NODE), flags(0), child() {
115 depends_on.priority = Priority();
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)
126 typedef base::hash_map<NodeId, Node> NodeMap;
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);
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;
145 NodeMap all_nodes_; // maps from node IDs to Node objects
147 DISALLOW_COPY_AND_ASSIGN(SpdyPriorityForest);
150 template <typename NodeId, typename Priority>
151 SpdyPriorityForest<NodeId, Priority>::SpdyPriorityForest() {}
153 template <typename NodeId, typename Priority>
154 SpdyPriorityForest<NodeId, Priority>::~SpdyPriorityForest() {}
156 template <typename NodeId, typename Priority>
157 int SpdyPriorityForest<NodeId, Priority>::num_nodes() const {
158 return all_nodes_.size();
161 template <typename NodeId, typename Priority>
162 bool SpdyPriorityForest<NodeId, Priority>::NodeExists(NodeId node_id) const {
163 return all_nodes_.count(node_id) != 0;
166 template <typename NodeId, typename Priority>
167 bool SpdyPriorityForest<NodeId, Priority>::AddRootNode(
168 NodeId node_id, Priority priority) {
169 if (NodeExists(node_id)) {
170 return false;
172 Node* new_node = &all_nodes_[node_id];
173 new_node->type = ROOT_NODE;
174 new_node->depends_on.priority = priority;
175 return true;
178 template <typename NodeId, typename Priority>
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;
185 Node* parent = &all_nodes_[parent_id];
186 if (parent->child != NodeId()) {
187 return false;
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;
197 template <typename NodeId, typename Priority>
198 bool SpdyPriorityForest<NodeId, Priority>::RemoveNode(NodeId node_id) {
199 if (!NodeExists(node_id)) {
200 return false;
202 const Node& node = all_nodes_[node_id];
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;
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;
232 // Delete the node.
233 all_nodes_.erase(node_id);
234 return true;
237 template <typename NodeId, typename Priority>
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();
248 template <typename NodeId, typename Priority>
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();
258 template <typename NodeId, typename Priority>
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;
265 template <typename NodeId, typename Priority>
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();
275 template <typename NodeId, typename Priority>
276 bool SpdyPriorityForest<NodeId, Priority>::SetPriority(
277 NodeId node_id, Priority priority) {
278 if (!NodeExists(node_id)) {
279 return false;
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;
291 node->depends_on.priority = priority;
292 return true;
295 template <typename NodeId, typename Priority>
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;
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;
310 // Otherwise, if the new parent already has a child, we fail.
311 if (new_parent->child != NodeId()) {
312 return false;
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];
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();
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;
344 template <typename NodeId, typename Priority>
345 bool SpdyPriorityForest<NodeId, Priority>::IsMarkedReadyToRead(
346 NodeId node_id) const {
347 return IsMarked(node_id, kReadyToRead);
350 template <typename NodeId, typename Priority>
351 bool SpdyPriorityForest<NodeId, Priority>::MarkReadyToRead(NodeId node_id) {
352 return Mark(node_id, kReadyToRead);
355 template <typename NodeId, typename Priority>
356 bool SpdyPriorityForest<NodeId, Priority>::MarkNoLongerReadyToRead(
357 NodeId node_id) {
358 return Unmark(node_id, kReadyToRead);
361 template <typename NodeId, typename Priority>
362 NodeId SpdyPriorityForest<NodeId, Priority>::NextNodeToRead() {
363 return FirstMarkedNode(kReadyToRead);
366 template <typename NodeId, typename Priority>
367 bool SpdyPriorityForest<NodeId, Priority>::IsMarkedReadyToWrite(
368 NodeId node_id) const {
369 return IsMarked(node_id, kReadyToWrite);
372 template <typename NodeId, typename Priority>
373 bool SpdyPriorityForest<NodeId, Priority>::MarkReadyToWrite(NodeId node_id) {
374 return Mark(node_id, kReadyToWrite);
377 template <typename NodeId, typename Priority>
378 bool SpdyPriorityForest<NodeId, Priority>::MarkNoLongerReadyToWrite(
379 NodeId node_id) {
380 return Unmark(node_id, kReadyToWrite);
383 template <typename NodeId, typename Priority>
384 NodeId SpdyPriorityForest<NodeId, Priority>::NextNodeToWrite() {
385 return FirstMarkedNode(kReadyToWrite);
388 template <typename NodeId, typename Priority>
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;
395 template <typename NodeId, typename Priority>
396 bool SpdyPriorityForest<NodeId, Priority>::Mark(
397 NodeId node_id, unsigned int flag) {
398 if (!NodeExists(node_id)) {
399 return false;
401 all_nodes_[node_id].flags |= flag;
402 return true;
405 template <typename NodeId, typename Priority>
406 bool SpdyPriorityForest<NodeId, Priority>::Unmark(
407 NodeId node_id, unsigned int flag) {
408 if (!NodeExists(node_id)) {
409 return false;
411 all_nodes_[node_id].flags &= ~flag;
412 return true;
415 template <typename NodeId, typename Priority>
416 NodeId SpdyPriorityForest<NodeId, Priority>::FirstMarkedNode(
417 unsigned int flag) {
418 // TODO(mdsteele): This is an *incredibly* stupid brute force solution.
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;
435 if (node->child == NodeId()) {
436 break;
438 DCHECK(NodeExists(node->child));
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());
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;
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;
474 if ((child->flags & flag) != 0) {
475 group.push_back(next->child);
477 next = child;
479 return group[base::RandGenerator(group.size())];
481 node_id = node->child;
485 template <typename NodeId, typename Priority>
486 const typename SpdyPriorityForest<NodeId, Priority>::Node*
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;
492 return &iter->second;
495 template <typename NodeId, typename Priority>
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;
506 if (node.child != NodeId()) {
507 if (!NodeExists(node.child) || node_id != GetParent(node.child)) {
508 return false;
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;
518 child_id = GetChild(child_id);
519 ++count;
522 return true;
525 } // namespace net
527 #endif // NET_SPDY_SPDY_PRIORITY_FOREST_H_