Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(131)

Side by Side Diff: net/spdy/http2_write_scheduler.h

Issue 1660283002: Rename and modify SpdyPriorityTree. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase on https://crrev.com/1668773003. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | net/spdy/http2_write_scheduler_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #ifndef NET_SPDY_HTTP2_WRITE_SCHEDULER_H_ 5 #ifndef NET_SPDY_HTTP2_WRITE_SCHEDULER_H_
6 #define NET_SPDY_HTTP2_WRITE_SCHEDULER_H_ 6 #define NET_SPDY_HTTP2_WRITE_SCHEDULER_H_
7 7
8 #include <stdint.h>
9
10 #include <algorithm>
8 #include <cmath> 11 #include <cmath>
9 #include <deque> 12 #include <deque>
10 #include <map> 13 #include <map>
11 #include <queue> 14 #include <queue>
12 #include <set> 15 #include <set>
16 #include <unordered_map>
13 #include <utility> 17 #include <utility>
14 #include <vector> 18 #include <vector>
15 19
16 #include "base/containers/hash_tables.h" 20 #include "base/containers/hash_tables.h"
21 #include "base/containers/linked_list.h"
17 #include "base/logging.h" 22 #include "base/logging.h"
18 #include "base/macros.h" 23 #include "base/macros.h"
19 #include "base/memory/scoped_ptr.h" 24 #include "base/memory/scoped_ptr.h"
20 #include "base/stl_util.h" 25 #include "base/stl_util.h"
21 26
22 namespace net { 27 namespace net {
23 28
24 // This data structure implements the HTTP/2 stream priority tree defined in 29 // This data structure implements the HTTP/2 stream priority tree defined in
25 // section 5.3 of RFC 7540: 30 // section 5.3 of RFC 7540:
26 // http://tools.ietf.org/html/rfc7540#section-5.3 31 // http://tools.ietf.org/html/rfc7540#section-5.3
27 // 32 //
28 // Nodes can be added and removed, and dependencies between them defined. 33 // Streams 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 34 // Streams constitute a tree rooted at stream ID 0: each stream has a single
30 // node, and 0 or more child nodes. Individual nodes can be marked as ready to 35 // parent stream, and 0 or more child streams. Individual streams can be
31 // read/write, and then the whole structure can be queried to pick the next 36 // marked as ready to read/write, and then the whole structure can be queried
32 // node to read/write out of those that are ready. 37 // to pick the next stream to read/write out of those that are ready.
33 // 38 //
34 // The NodeId type must be a POD that supports comparison (most 39 // The StreamIdType type must be a POD that supports comparison (most
35 // likely, it will be a number). 40 // likely, it will be a number).
36 41
37 namespace test { 42 namespace test {
38 template <typename NodeId> 43 template <typename StreamIdType>
39 class SpdyPriorityTreePeer; 44 class Http2PriorityWriteSchedulerPeer;
40 } 45 }
41 46
42 const int kRootNodeId = 0; 47 const unsigned int kHttp2RootStreamId = 0;
43 const int kDefaultWeight = 16; 48 const int kHttp2DefaultStreamWeight = 16;
44 const int kMinWeight = 1; 49 const int kHttp2MinStreamWeight = 1;
45 const int kMaxWeight = 256; 50 const int kHttp2MaxStreamWeight = 256;
46 51
47 template <typename NodeId> 52 template <typename StreamIdType>
48 class SpdyPriorityTree { 53 class Http2PriorityWriteScheduler {
49 public: 54 public:
50 typedef std::pair<NodeId, float> PriorityNode; 55 Http2PriorityWriteScheduler();
51 typedef std::vector<PriorityNode> PriorityList; 56
52 57 friend class test::Http2PriorityWriteSchedulerPeer<StreamIdType>;
53 SpdyPriorityTree(); 58
54 59 // Return the number of streams currently in the tree.
55 // Orders in descending order of priority. 60 int num_streams() const;
56 struct NodePriorityComparator { 61
57 bool operator ()(const std::pair<NodeId, float>& lhs, 62 // Return true if the tree contains a stream with the given ID.
58 const std::pair<NodeId, float>& rhs); 63 bool StreamRegistered(StreamIdType stream_id) const;
59 }; 64
60 65 // Registers a new stream with the given weight and parent, adding it to the
61 friend class test::SpdyPriorityTreePeer<NodeId>; 66 // dependency tree. Non-exclusive streams simply get added below the parent
62 67 // stream. If exclusive = true, the stream becomes the parent's sole child
63 // Return the number of nodes currently in the tree. 68 // and the parent's previous children become the children of the new
64 int num_nodes() const; 69 // stream. If the stream was already registered, logs DFATAL and does
65 70 // nothing. If the parent stream is not registered, logs DFATAL and uses the
66 // Return true if the tree contains a node with the given ID. 71 // root stream as the parent.
67 bool NodeExists(NodeId node_id) const; 72 void RegisterStream(StreamIdType stream_id,
68 73 StreamIdType parent_id,
69 // Add a new node with the given weight and parent. Non-exclusive nodes 74 int weight,
70 // simply get added below the parent node. If exclusive = true, the node 75 bool exclusive);
71 // becomes the parent's sole child and the parent's previous children 76
72 // become the children of the new node. 77 // Unregisters the given stream from the scheduler, removing it from the
73 // Returns true on success. Returns false if the node already exists 78 // dependency tree. If the stream was not previously registered, logs DFATAL
74 // in the tree, or if the parent node does not exist. 79 // and does nothing.
75 bool AddNode(NodeId node_id, NodeId parent_id, int weight, bool exclusive); 80 void UnregisterStream(StreamIdType stream_id);
76 81
77 // Remove an existing node from the tree. Returns true on success, or 82 // Returns the weight value for the specified stream. If the stream is not
78 // false if the node doesn't exist. 83 // registered, logs DFATAL and returns the lowest weight.
79 bool RemoveNode(NodeId node_id); 84 int GetStreamWeight(StreamIdType stream_id) const;
80 85
81 // Get the weight of the given node. 86 // Returns the stream ID for the parent of the given stream. If the stream
82 int GetWeight(NodeId node_id) const; 87 // isn't registered, logs DFATAL and returns the root stream ID (0).
83 88 StreamIdType GetStreamParent(StreamIdType stream_id) const;
84 // Get the parent of the given node. If the node doesn't exist, or is a root 89
85 // node (and thus has no parent), returns NodeId(). 90 // Returns stream IDs of the children of the given stream, if any. If the
86 NodeId GetParent(NodeId node_id) const; 91 // stream isn't registered, logs DFATAL and returns an empty vector.
87 92 std::vector<StreamIdType> GetStreamChildren(StreamIdType stream_id) const;
88 // Get the children of the given node. If the node doesn't exist, or has no 93
89 // child, returns empty vector. 94 // Sets the weight of the given stream. If the stream isn't registered or is
90 std::vector<NodeId> GetChildren(NodeId node_id) const; 95 // the root stream, logs DFATAL and does nothing.
91 96 void SetStreamWeight(StreamIdType stream_id, int weight);
92 // Set the priority of the given node. 97
93 bool SetWeight(NodeId node_id, int weight); 98 // Sets the parent of the given stream. If the stream and/or parent aren't
94 99 // registered, logs DFATAL and does nothing. If the new parent is a
95 // Set the parent of the given node. Returns true on success. 100 // descendant of the stream (i.e. this would have created a cycle) then the
96 // Returns false and has no effect if the node and/or the parent doesn't 101 // topology of the tree is rearranged as described in section 5.3.3 of RFC
97 // exist. If the new parent is a descendant of the node (i.e. this would have 102 // 7540: https://tools.ietf.org/html/rfc7540#section-5.3.3
98 // created a cycle) then we rearrange the topology of the tree as described 103 void SetStreamParent(StreamIdType stream_id,
99 // in section 5.3.3 of RFC 7540: 104 StreamIdType parent_id,
100 // https://tools.ietf.org/html/rfc7540#section-5.3.3 105 bool exclusive);
101 bool SetParent(NodeId node_id, NodeId parent_id, bool exclusive); 106
102 107 // Returns true if the stream parent_id has child_id in its children. If
103 // Returns true if the node parent_id has child_id in its children. 108 // either parent or child stream aren't registered, logs DFATAL and returns
104 bool HasChild(NodeId parent_id, NodeId child_id) const; 109 // false.
105 110 bool StreamHasChild(StreamIdType parent_id, StreamIdType child_id) const;
106 // Mark a node as blocked or unblocked. Return true on success, or false 111
107 // if unable to mark the specified node. 112 // Marks the stream as blocked or unblocked. If the stream is not registered,
108 bool SetBlocked(NodeId node_id, bool blocked); 113 // logs DFATAL and does nothing.
109 114 void MarkStreamBlocked(StreamIdType stream_id, bool blocked);
110 // Mark whether or not a node is ready to write; i.e. whether there is 115
111 // buffered data for the associated stream. Return true on success, or false 116 // Marks the stream as ready or not ready to write; i.e. whether there is
112 // if unable to mark the specified node. 117 // buffered data for the associated stream. If the stream is not registered,
113 bool SetReady(NodeId node_id, bool ready); 118 // logs DFATAL and does nothing.
114 119 void MarkStreamReady(StreamIdType stream_id, bool ready);
115 // Returns an ordered list of writeable nodes and their priorities. 120
116 // Priority is calculated as: 121 // Returns true iff the scheduler has one or more usable streams. A stream is
117 // parent's priority * (node's weight / sum of sibling weights) 122 // usable if it has ready == true and blocked == false, and is not the direct
118 PriorityList GetPriorityList(); 123 // or indirect child of another stream that itself has ready == true and
124 // blocked == false. (Note that the root stream always has ready == false.)
125 bool HasUsableStreams() const;
126
127 // If the scheduler has any usable streams, returns the ID of the next usable
128 // stream, in the process changing its ready state to false. If the scheduler
129 // does not have any usable streams, logs DFATAL and returns the root stream
130 // ID (0). If there are multiple usable streams, precedence is given to the
131 // one with the highest priority (thus preserving SPDY priority semantics),
132 // or, if there are multiple with the highest priority, the one with the
133 // lowest ordinal (ensuring round-robin ordering).
134 StreamIdType PopNextUsableStream();
119 135
120 private: 136 private:
121 struct Node; 137 struct StreamInfo;
122 typedef std::vector<Node*> NodeVector; 138 using StreamInfoVector = std::vector<StreamInfo*>;
123 typedef std::map<NodeId, Node*> NodeMap; 139 using StreamInfoMap = std::unordered_map<StreamIdType, StreamInfo*>;
124 140
125 struct Node { 141 struct StreamInfo : public base::LinkNode<StreamInfo> {
126 // ID for this node. 142 // ID for this stream.
127 NodeId id; 143 StreamIdType id;
128 // ID of parent node. 144 // ID of parent stream.
129 Node* parent = nullptr; 145 StreamInfo* parent = nullptr;
130 // Weights can range between 1 and 256 (inclusive). 146 // Weights can range between 1 and 256 (inclusive).
131 int weight = kDefaultWeight; 147 int weight = kHttp2DefaultStreamWeight;
132 // The total weight of this node's direct descendants. 148 // The total weight of this stream's direct descendants.
133 int total_child_weights = 0; 149 int total_child_weights = 0;
134 // The total weight of direct descendants that are writeable 150 // Pointers to StreamInfos for children, if any.
135 // (ready to write and not blocked). This value does not necessarily 151 StreamInfoVector children;
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? 152 // Is the associated stream write-blocked?
142 bool blocked = false; 153 bool blocked = false;
143 // Does the stream have data ready for writing? 154 // Does the stream have data ready for writing?
144 bool ready = false; 155 bool ready = false;
145 // The fraction of resources to dedicate to this node. 156 // Whether the stream is currently present in scheduling_queue_.
157 bool scheduled = false;
158 // The scheduling priority of this stream. Streams with higher priority
159 // values are scheduled first.
146 float priority = 0; 160 float priority = 0;
161 // Ordinal value for this stream, used to ensure round-robin scheduling:
162 // among streams with the same scheduling priority, streams with lower
163 // ordinal are scheduled first. The ordinal is reset to a new, greater
164 // value when the stream is next inserted into scheduling_queue_.
165 int64_t ordinal = 0;
166
167 // Whether the stream ought to be in scheduling_queue_.
168 bool IsSchedulable() const { return ready && !blocked; }
169
170 // Whether this stream should be scheduled ahead of another stream.
171 bool SchedulesBefore(const StreamInfo& other) const {
172 return (priority != other.priority) ? priority > other.priority
173 : ordinal < other.ordinal;
174 }
147 }; 175 };
148 176
149 static bool Remove(NodeVector* nodes, const Node* node); 177 static bool Remove(StreamInfoVector* stream_infos,
150 178 const StreamInfo* stream_info);
151 // Update the value of total_writeable_child_weights for the given node 179
152 // to reflect the current state of the tree. 180 // Clamps weight to a value in [kHttp2MinStreamWeight,
153 void PropagateNodeState(Node* node); 181 // kHttp2MaxStreamWeight].
154 182 static int ClampWeight(int weight);
155 // Get the given node, or return nullptr if it doesn't exist. 183
156 const Node* FindNode(NodeId node_id) const; 184 // Returns true iff any direct or transitive parent of the given stream is
157 Node* FindNode(NodeId node_id); 185 // currently scheduled.
186 static bool HasScheduledAncestor(const StreamInfo& stream_info);
187
188 // Returns StreamInfo for the given stream, or nullptr if it isn't
189 // registered.
190 const StreamInfo* FindStream(StreamIdType stream_id) const;
191 StreamInfo* FindStream(StreamIdType stream_id);
192
193 // Update all priority values in the subtree rooted at the given stream, not
194 // including the stream itself. If this results in priority value changes for
195 // scheduled streams, those streams are rescheduled to ensure proper ordering
196 // of scheduling_queue_.
197 void UpdatePrioritiesUnder(StreamInfo* stream_info);
198
199 // Adds or removes stream from scheduling_queue_ according to whether it is
200 // schedulable. If stream is newly schedulable, assigns it the next
201 // (increasing) ordinal value.
202 void UpdateScheduling(StreamInfo* stream_info);
203
204 // Inserts stream into scheduling_queue_ at the appropriate location given
205 // its priority and ordinal. Time complexity is O(scheduling_queue.size()).
206 void Schedule(StreamInfo* stream_info);
207
208 // Removes stream from scheduling_queue_.
209 void Unschedule(StreamInfo* stream_info);
158 210
159 // Return true if all internal invariants hold (useful for unit tests). 211 // Return true if all internal invariants hold (useful for unit tests).
160 // Unless there are bugs, this should always return true. 212 // Unless there are bugs, this should always return true.
161 bool ValidateInvariantsForTests() const; 213 bool ValidateInvariantsForTests() const;
162 214
163 Node* root_node_; // pointee owned by all_nodes_ 215 // Pointee owned by all_stream_infos_.
164 NodeMap all_nodes_; // maps from node IDs to Node objects 216 StreamInfo* root_stream_info_;
165 STLValueDeleter<NodeMap> all_nodes_deleter_; 217 // Maps from stream IDs to StreamInfo objects.
166 218 StreamInfoMap all_stream_infos_;
167 DISALLOW_COPY_AND_ASSIGN(SpdyPriorityTree); 219 STLValueDeleter<StreamInfoMap> all_stream_infos_deleter_;
220 // Queue containing all streams that are ready and unblocked, ordered with
221 // streams of higher priority before streams of lower priority, and, among
222 // streams of equal priority, streams with lower ordinal before those with
223 // higher ordinal. Note that not all streams in scheduling_queue_ are
224 // necessarily usable: some may have ancestor stream(s) that are ready and
225 // unblocked. In these situations the occluded child streams are left in the
226 // queue, to reduce churn.
227 base::LinkedList<StreamInfo> scheduling_queue_;
228 // Ordinal value to assign to next node inserted into
229 // scheduling_queue_. Incremented after each insertion.
230 int64_t next_ordinal_ = 0;
231
232 DISALLOW_COPY_AND_ASSIGN(Http2PriorityWriteScheduler);
168 }; 233 };
169 234
170 template <typename NodeId> 235 template <typename StreamIdType>
171 SpdyPriorityTree<NodeId>::SpdyPriorityTree() 236 Http2PriorityWriteScheduler<StreamIdType>::Http2PriorityWriteScheduler()
172 : all_nodes_deleter_(&all_nodes_) { 237 : all_stream_infos_deleter_(&all_stream_infos_) {
173 root_node_ = new Node(); 238 root_stream_info_ = new StreamInfo();
174 root_node_->id = kRootNodeId; 239 root_stream_info_->id = kHttp2RootStreamId;
175 root_node_->weight = kDefaultWeight; 240 root_stream_info_->weight = kHttp2DefaultStreamWeight;
176 root_node_->parent = nullptr; 241 root_stream_info_->parent = nullptr;
177 root_node_->priority = 1.0; 242 root_stream_info_->priority = 1.0;
178 root_node_->ready = true; 243 root_stream_info_->ready = true;
179 all_nodes_[kRootNodeId] = root_node_; 244 all_stream_infos_[kHttp2RootStreamId] = root_stream_info_;
180 } 245 }
181 246
182 template <typename NodeId> 247 template <typename StreamIdType>
183 bool SpdyPriorityTree<NodeId>::NodePriorityComparator::operator()( 248 int Http2PriorityWriteScheduler<StreamIdType>::num_streams() const {
184 const std::pair<NodeId, float>& lhs, 249 return all_stream_infos_.size();
185 const std::pair<NodeId, float>& rhs) { 250 }
186 return lhs.second > rhs.second; 251
187 } 252 template <typename StreamIdType>
188 253 bool Http2PriorityWriteScheduler<StreamIdType>::StreamRegistered(
189 template <typename NodeId> 254 StreamIdType stream_id) const {
190 int SpdyPriorityTree<NodeId>::num_nodes() const { 255 return ContainsKey(all_stream_infos_, stream_id);
191 return all_nodes_.size(); 256 }
192 } 257
193 258 template <typename StreamIdType>
194 template <typename NodeId> 259 void Http2PriorityWriteScheduler<StreamIdType>::RegisterStream(
195 bool SpdyPriorityTree<NodeId>::NodeExists(NodeId node_id) const { 260 StreamIdType stream_id,
196 return ContainsKey(all_nodes_, node_id); 261 StreamIdType parent_id,
197 } 262 int weight,
198 263 bool exclusive) {
199 template <typename NodeId> 264 if (StreamRegistered(stream_id)) {
200 bool SpdyPriorityTree<NodeId>::AddNode(NodeId node_id, 265 LOG(DFATAL) << "Stream " << stream_id << " already registered";
201 NodeId parent_id, 266 return;
202 int weight, 267 }
203 bool exclusive) { 268 weight = ClampWeight(weight);
204 if (NodeExists(node_id) || weight < kMinWeight || weight > kMaxWeight) { 269
205 return false; 270 StreamInfo* parent = FindStream(parent_id);
206 }
207 Node* parent = FindNode(parent_id);
208 if (parent == nullptr) { 271 if (parent == nullptr) {
209 return false; 272 LOG(DFATAL) << "Parent stream " << parent_id << " not registered";
210 } 273 parent = root_stream_info_;
211 Node* new_node = new Node; 274 }
212 new_node->id = node_id; 275
213 new_node->weight = weight; 276 StreamInfo* new_stream_info = new StreamInfo;
214 new_node->parent = parent; 277 new_stream_info->id = stream_id;
215 all_nodes_[node_id] = new_node; 278 new_stream_info->weight = weight;
279 new_stream_info->parent = parent;
280 all_stream_infos_[stream_id] = new_stream_info;
216 if (exclusive) { 281 if (exclusive) {
217 // Move the parent's current children below the new node. 282 // Move the parent's current children below the new stream.
218 using std::swap; 283 using std::swap;
219 swap(new_node->children, parent->children); 284 swap(new_stream_info->children, parent->children);
220 new_node->total_child_weights = parent->total_child_weights; 285 new_stream_info->total_child_weights = parent->total_child_weights;
221 // Update each child's parent. 286 // Update each child's parent.
222 for (Node* child : new_node->children) { 287 for (StreamInfo* child : new_stream_info->children) {
223 child->parent = new_node; 288 child->parent = new_stream_info;
224 } 289 }
225 // Clear parent's old child data. 290 // Clear parent's old child data.
226 DCHECK(parent->children.empty()); 291 DCHECK(parent->children.empty());
227 parent->total_child_weights = 0; 292 parent->total_child_weights = 0;
228 } 293 }
229 // Add new node to parent. 294 // Add new stream to parent.
230 parent->children.push_back(new_node); 295 parent->children.push_back(new_stream_info);
231 parent->total_child_weights += weight; 296 parent->total_child_weights += weight;
232 return true; 297
233 } 298 // Update all priorities under parent, since addition of a stream affects
234 299 // sibling priorities as well.
235 template <typename NodeId> 300 UpdatePrioritiesUnder(parent);
236 bool SpdyPriorityTree<NodeId>::RemoveNode(NodeId node_id) { 301
237 if (node_id == kRootNodeId) { 302 // Stream starts with ready == false, so no need to schedule it yet.
238 return false; 303 DCHECK(!new_stream_info->IsSchedulable());
239 } 304 }
240 // Remove the node from table. 305
241 typename NodeMap::iterator it = all_nodes_.find(node_id); 306 template <typename StreamIdType>
242 if (it == all_nodes_.end()) { 307 void Http2PriorityWriteScheduler<StreamIdType>::UnregisterStream(
243 return false; 308 StreamIdType stream_id) {
244 } 309 if (stream_id == kHttp2RootStreamId) {
245 scoped_ptr<Node> node(it->second); 310 LOG(DFATAL) << "Cannot unregister root stream";
246 all_nodes_.erase(it); 311 return;
247 312 }
248 Node* parent = node->parent; 313 // Remove the stream from table.
249 // Remove the node from parent's child list. 314 typename StreamInfoMap::iterator it = all_stream_infos_.find(stream_id);
250 Remove(&parent->children, node.get()); 315 if (it == all_stream_infos_.end()) {
251 parent->total_child_weights -= node->weight; 316 LOG(DFATAL) << "Stream " << stream_id << " not registered";
252 317 return;
253 // Move the node's children to the parent's child list. 318 }
319 scoped_ptr<StreamInfo> stream_info(std::move(it->second));
320 all_stream_infos_.erase(it);
321 // If scheduled, unschedule.
322 if (stream_info->scheduled) {
323 Unschedule(stream_info.get());
324 }
325
326 StreamInfo* parent = stream_info->parent;
327 // Remove the stream from parent's child list.
328 Remove(&parent->children, stream_info.get());
329 parent->total_child_weights -= stream_info->weight;
330
331 // Move the stream's children to the parent's child list.
254 // Update each child's parent and weight. 332 // Update each child's parent and weight.
255 for (Node* child : node->children) { 333 for (StreamInfo* child : stream_info->children) {
256 child->parent = parent; 334 child->parent = parent;
257 parent->children.push_back(child); 335 parent->children.push_back(child);
258 // Divide the removed node's weight among its children, rounding to the 336 // Divide the removed stream's weight among its children, rounding to the
259 // nearest valid weight. 337 // nearest valid weight.
260 float float_weight = node->weight * static_cast<float>(child->weight) / 338 float float_weight = stream_info->weight *
261 static_cast<float>(node->total_child_weights); 339 static_cast<float>(child->weight) /
340 static_cast<float>(stream_info->total_child_weights);
262 int new_weight = floor(float_weight + 0.5); 341 int new_weight = floor(float_weight + 0.5);
263 if (new_weight == 0) { 342 if (new_weight == 0) {
264 new_weight = 1; 343 new_weight = 1;
265 } 344 }
266 child->weight = new_weight; 345 child->weight = new_weight;
267 parent->total_child_weights += child->weight; 346 parent->total_child_weights += child->weight;
268 } 347 }
269 348 UpdatePrioritiesUnder(parent);
270 return true;
271 } 349 }
272 350
273 template <typename NodeId> 351 template <typename StreamIdType>
274 int SpdyPriorityTree<NodeId>::GetWeight(NodeId node_id) const { 352 int Http2PriorityWriteScheduler<StreamIdType>::GetStreamWeight(
275 const Node* node = FindNode(node_id); 353 StreamIdType stream_id) const {
276 return (node == nullptr) ? 0 : node->weight; 354 const StreamInfo* stream_info = FindStream(stream_id);
355 if (stream_info == nullptr) {
356 LOG(DFATAL) << "Stream " << stream_id << " not registered";
357 return kHttp2MinStreamWeight;
358 }
359 return stream_info->weight;
277 } 360 }
278 361
279 template <typename NodeId> 362 template <typename StreamIdType>
280 NodeId SpdyPriorityTree<NodeId>::GetParent(NodeId node_id) const { 363 StreamIdType Http2PriorityWriteScheduler<StreamIdType>::GetStreamParent(
281 const Node* node = FindNode(node_id); 364 StreamIdType stream_id) const {
282 // Root node has null parent. 365 const StreamInfo* stream_info = FindStream(stream_id);
283 return (node == nullptr || node->parent == nullptr) ? kRootNodeId 366 if (stream_info == nullptr) {
284 : node->parent->id; 367 LOG(DFATAL) << "Stream " << stream_id << " not registered";
368 return kHttp2RootStreamId;
369 }
370 if (stream_info->parent == nullptr) { // root stream
371 return kHttp2RootStreamId;
372 }
373 return stream_info->parent->id;
285 } 374 }
286 375
287 template <typename NodeId> 376 template <typename StreamIdType>
288 std::vector<NodeId> SpdyPriorityTree<NodeId>::GetChildren( 377 std::vector<StreamIdType> Http2PriorityWriteScheduler<
289 NodeId node_id) const { 378 StreamIdType>::GetStreamChildren(StreamIdType stream_id) const {
290 std::vector<NodeId> child_vec; 379 std::vector<StreamIdType> child_vec;
291 const Node* node = FindNode(node_id); 380 const StreamInfo* stream_info = FindStream(stream_id);
292 if (node != nullptr) { 381 if (stream_info == nullptr) {
293 child_vec.reserve(node->children.size()); 382 LOG(DFATAL) << "Stream " << stream_id << " not registered";
294 for (Node* child : node->children) { 383 } else {
384 child_vec.reserve(stream_info->children.size());
385 for (StreamInfo* child : stream_info->children) {
295 child_vec.push_back(child->id); 386 child_vec.push_back(child->id);
296 } 387 }
297 } 388 }
298 return child_vec; 389 return child_vec;
299 } 390 }
300 391
301 template <typename NodeId> 392 template <typename StreamIdType>
302 bool SpdyPriorityTree<NodeId>::SetWeight( 393 void Http2PriorityWriteScheduler<StreamIdType>::SetStreamWeight(
303 NodeId node_id, int weight) { 394 StreamIdType stream_id,
304 if (!NodeExists(node_id)) { 395 int weight) {
305 return false; 396 if (stream_id == kHttp2RootStreamId) {
397 LOG(DFATAL) << "Cannot set weight of root stream";
398 return;
306 } 399 }
307 if (weight < kMinWeight || weight > kMaxWeight) { 400 StreamInfo* stream_info = FindStream(stream_id);
308 return false; 401 if (stream_info == nullptr) {
402 LOG(DFATAL) << "Stream " << stream_id << " not registered";
403 return;
404 }
405 weight = ClampWeight(weight);
406 if (weight == stream_info->weight) {
407 return;
408 }
409 if (stream_info->parent != nullptr) {
410 stream_info->parent->total_child_weights += (weight - stream_info->weight);
411 }
412 stream_info->weight = weight;
413
414 // Change in weight also affects sibling priorities.
415 UpdatePrioritiesUnder(stream_info->parent);
416 }
417
418 template <typename StreamIdType>
419 void Http2PriorityWriteScheduler<StreamIdType>::SetStreamParent(
420 StreamIdType stream_id,
421 StreamIdType parent_id,
422 bool exclusive) {
423 if (stream_id == kHttp2RootStreamId) {
424 LOG(DFATAL) << "Cannot set parent of root stream";
425 return;
426 }
427 if (stream_id == parent_id) {
428 LOG(DFATAL) << "Cannot set stream to be its own parent";
429 return;
430 }
431 StreamInfo* stream_info = FindStream(stream_id);
432 if (stream_info == nullptr) {
433 LOG(DFATAL) << "Stream " << stream_id << " not registered";
434 return;
435 }
436 StreamInfo* new_parent = FindStream(parent_id);
437 if (new_parent == nullptr) {
438 LOG(DFATAL) << "Parent stream " << parent_id << " not registered";
439 return;
309 } 440 }
310 441
311 Node* node = all_nodes_[node_id]; 442 // If the new parent is already the stream's parent, we're done.
312 if (node->parent != nullptr) { 443 if (stream_info->parent == new_parent) {
313 node->parent->total_child_weights += (weight - node->weight); 444 return;
314 }
315 node->weight = weight;
316
317 return true;
318 }
319
320
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;
326 }
327 Node* node = FindNode(node_id);
328 Node* new_parent = FindNode(parent_id);
329 if (node == nullptr || new_parent == nullptr) {
330 return false;
331 }
332
333 // If the new parent is already the node's parent, we're done.
334 if (node->parent == new_parent) {
335 return true;
336 } 445 }
337 446
338 // Next, check to see if the new parent is currently a descendant 447 // Next, check to see if the new parent is currently a descendant
339 // of the node. 448 // of the stream.
340 Node* last = new_parent->parent; 449 StreamInfo* last = new_parent->parent;
341 bool cycle_exists = false; 450 bool cycle_exists = false;
342 while (last != nullptr) { 451 while (last != nullptr) {
343 if (last == node) { 452 if (last == stream_info) {
344 cycle_exists = true; 453 cycle_exists = true;
345 break; 454 break;
346 } 455 }
347 last = last->parent; 456 last = last->parent;
348 } 457 }
349 458
350 if (cycle_exists) { 459 if (cycle_exists) {
351 // The new parent moves to the level of the current node. 460 // The new parent moves to the level of the current stream.
352 SetParent(parent_id, node->parent->id, false); 461 SetStreamParent(parent_id, stream_info->parent->id, false);
353 } 462 }
354 463
355 // Remove node from old parent's child list. 464 // Remove stream from old parent's child list.
356 Node* old_parent = node->parent; 465 StreamInfo* old_parent = stream_info->parent;
357 Remove(&old_parent->children, node); 466 Remove(&old_parent->children, stream_info);
358 old_parent->total_child_weights -= node->weight; 467 old_parent->total_child_weights -= stream_info->weight;
468 UpdatePrioritiesUnder(old_parent);
359 469
360 if (exclusive) { 470 if (exclusive) {
361 // Move the new parent's current children below the current node. 471 // Move the new parent's current children below the current stream.
362 for (Node* child : new_parent->children) { 472 for (StreamInfo* child : new_parent->children) {
363 child->parent = node; 473 child->parent = stream_info;
364 node->children.push_back(child); 474 stream_info->children.push_back(child);
365 } 475 }
366 node->total_child_weights += new_parent->total_child_weights; 476 stream_info->total_child_weights += new_parent->total_child_weights;
367 // Clear new parent's old child data. 477 // Clear new parent's old child data.
368 new_parent->children.clear(); 478 new_parent->children.clear();
369 new_parent->total_child_weights = 0; 479 new_parent->total_child_weights = 0;
370 } 480 }
371 481
372 // Make the change. 482 // Make the change.
373 node->parent = new_parent; 483 stream_info->parent = new_parent;
374 new_parent->children.push_back(node); 484 new_parent->children.push_back(stream_info);
375 new_parent->total_child_weights += node->weight; 485 new_parent->total_child_weights += stream_info->weight;
376 return true; 486 UpdatePrioritiesUnder(new_parent);
377 } 487 }
378 488
379 template <typename NodeId> 489 template <typename StreamIdType>
380 bool SpdyPriorityTree<NodeId>::SetBlocked(NodeId node_id, bool blocked) { 490 void Http2PriorityWriteScheduler<StreamIdType>::MarkStreamBlocked(
381 if (!NodeExists(node_id)) { 491 StreamIdType stream_id,
492 bool blocked) {
493 if (stream_id == kHttp2RootStreamId) {
494 LOG(DFATAL) << "Cannot mark root stream blocked or unblocked";
495 return;
496 }
497 StreamInfo* stream_info = FindStream(stream_id);
498 if (stream_info == nullptr) {
499 LOG(DFATAL) << "Stream " << stream_id << " not registered";
500 return;
501 }
502 stream_info->blocked = blocked;
503 UpdateScheduling(stream_info);
504 }
505
506 template <typename StreamIdType>
507 void Http2PriorityWriteScheduler<StreamIdType>::MarkStreamReady(
508 StreamIdType stream_id,
509 bool ready) {
510 if (stream_id == kHttp2RootStreamId) {
511 LOG(DFATAL) << "Cannot mark root stream ready or unready";
512 return;
513 }
514 StreamInfo* stream_info = FindStream(stream_id);
515 if (stream_info == nullptr) {
516 LOG(DFATAL) << "Stream " << stream_id << " not registered";
517 return;
518 }
519 stream_info->ready = ready;
520 UpdateScheduling(stream_info);
521 }
522
523 template <typename StreamIdType>
524 bool Http2PriorityWriteScheduler<StreamIdType>::Remove(
525 StreamInfoVector* stream_infos,
526 const StreamInfo* stream_info) {
527 for (typename StreamInfoVector::iterator it = stream_infos->begin();
528 it != stream_infos->end(); ++it) {
529 if (*it == stream_info) {
530 stream_infos->erase(it);
531 return true;
532 }
533 }
534 return false;
535 }
536
537 template <typename StreamIdType>
538 int Http2PriorityWriteScheduler<StreamIdType>::ClampWeight(int weight) {
539 if (weight < kHttp2MinStreamWeight) {
540 LOG(DFATAL) << "Invalid weight: " << weight;
541 return kHttp2MinStreamWeight;
542 }
543 if (weight > kHttp2MaxStreamWeight) {
544 LOG(DFATAL) << "Invalid weight: " << weight;
545 return kHttp2MaxStreamWeight;
546 }
547 return weight;
548 }
549
550 template <typename StreamIdType>
551 bool Http2PriorityWriteScheduler<StreamIdType>::HasScheduledAncestor(
552 const StreamInfo& stream_info) {
553 for (const StreamInfo* parent = stream_info.parent; parent != nullptr;
554 parent = parent->parent) {
555 if (parent->scheduled) {
556 return true;
557 }
558 }
559 return false;
560 }
561
562 template <typename StreamIdType>
563 const typename Http2PriorityWriteScheduler<StreamIdType>::StreamInfo*
564 Http2PriorityWriteScheduler<StreamIdType>::FindStream(
565 StreamIdType stream_id) const {
566 typename StreamInfoMap::const_iterator it = all_stream_infos_.find(stream_id);
567 return it == all_stream_infos_.end() ? nullptr : it->second;
568 }
569
570 template <typename StreamIdType>
571 typename Http2PriorityWriteScheduler<StreamIdType>::StreamInfo*
572 Http2PriorityWriteScheduler<StreamIdType>::FindStream(StreamIdType stream_id) {
573 typename StreamInfoMap::iterator it = all_stream_infos_.find(stream_id);
574 return it == all_stream_infos_.end() ? nullptr : it->second;
575 }
576
577 template <typename StreamIdType>
578 void Http2PriorityWriteScheduler<StreamIdType>::UpdatePrioritiesUnder(
579 StreamInfo* stream_info) {
580 for (StreamInfo* child : stream_info->children) {
581 child->priority = stream_info->priority *
582 (static_cast<float>(child->weight) /
583 static_cast<float>(stream_info->total_child_weights));
584 if (child->scheduled) {
585 // Reposition in scheduling_queue_. Use post-order for scheduling, to
586 // benefit from the fact that children have priority <= parent priority.
587 Unschedule(child);
588 UpdatePrioritiesUnder(child);
589 Schedule(child);
590 } else {
591 UpdatePrioritiesUnder(child);
592 }
593 }
594 }
595
596 template <typename StreamIdType>
597 void Http2PriorityWriteScheduler<StreamIdType>::UpdateScheduling(
598 StreamInfo* stream_info) {
599 if (stream_info->IsSchedulable() != stream_info->scheduled) {
600 if (stream_info->scheduled) {
601 Unschedule(stream_info);
602 } else {
603 stream_info->ordinal = next_ordinal_++;
604 Schedule(stream_info);
605 }
606 }
607 }
608
609 template <typename StreamIdType>
610 void Http2PriorityWriteScheduler<StreamIdType>::Schedule(
611 StreamInfo* stream_info) {
612 DCHECK(!stream_info->scheduled);
613 for (base::LinkNode<StreamInfo>* s = scheduling_queue_.head();
614 s != scheduling_queue_.end(); s = s->next()) {
615 if (stream_info->SchedulesBefore(*s->value())) {
616 stream_info->InsertBefore(s);
617 stream_info->scheduled = true;
618 break;
619 }
620 }
621 if (!stream_info->scheduled) {
622 stream_info->InsertAfter(scheduling_queue_.tail());
623 stream_info->scheduled = true;
624 }
625 }
626
627 template <typename StreamIdType>
628 void Http2PriorityWriteScheduler<StreamIdType>::Unschedule(
629 StreamInfo* stream_info) {
630 DCHECK(stream_info->scheduled);
631 stream_info->RemoveFromList();
632 stream_info->scheduled = false;
633 }
634
635 template <typename StreamIdType>
636 bool Http2PriorityWriteScheduler<StreamIdType>::StreamHasChild(
637 StreamIdType parent_id,
638 StreamIdType child_id) const {
639 const StreamInfo* parent = FindStream(parent_id);
640 if (parent == nullptr) {
641 LOG(DFATAL) << "Parent stream " << parent_id << " not registered";
382 return false; 642 return false;
383 } 643 }
384 644 if (!StreamRegistered(child_id)) {
385 Node* node = all_nodes_[node_id]; 645 LOG(DFATAL) << "Child stream " << child_id << " not registered";
386 node->blocked = blocked;
387 return true;
388 }
389
390 template <typename NodeId>
391 bool SpdyPriorityTree<NodeId>::SetReady(NodeId node_id, bool ready) {
392 if (!NodeExists(node_id)) {
393 return false; 646 return false;
394 } 647 }
395 Node* node = all_nodes_[node_id]; 648 auto found = std::find_if(parent->children.begin(), parent->children.end(),
396 node->ready = ready; 649 [child_id](StreamInfo* stream_info) {
397 return true; 650 return stream_info->id == child_id;
398 } 651 });
399
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;
407 }
408 }
409 return false;
410 }
411
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);
418 }
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;
423 }
424 }
425
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);
431 }
432
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);
438 }
439
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;
446 }
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(); 652 return found != parent->children.end();
451 } 653 }
452 654
453 template <typename NodeId> 655 template <typename StreamIdType>
454 std::vector<std::pair<NodeId, float> > 656 bool Http2PriorityWriteScheduler<StreamIdType>::HasUsableStreams() const {
455 SpdyPriorityTree<NodeId>::GetPriorityList() { 657 // Even though not every stream in scheduling queue is guaranteed to be
456 PriorityList priority_list; 658 // usable (since children are occluded by parents), the presence of any
457 659 // streams guarantees at least one is usable.
458 // Update total_writeable_child_weights to reflect the current 660 return !scheduling_queue_.empty();
459 // state of the tree. 661 }
460 PropagateNodeState(root_node_); 662
461 663 template <typename StreamIdType>
462 std::deque<Node*> queue; 664 StreamIdType Http2PriorityWriteScheduler<StreamIdType>::PopNextUsableStream() {
463 DCHECK(root_node_->priority == 1.0); 665 for (base::LinkNode<StreamInfo>* s = scheduling_queue_.head();
464 // Start by examining our top-level nodes. 666 s != scheduling_queue_.end(); s = s->next()) {
465 for (Node* child : root_node_->children) { 667 StreamInfo* stream_info = s->value();
466 queue.push_back(child); 668 if (!HasScheduledAncestor(*stream_info)) {
467 } 669 stream_info->ready = false;
468 while (!queue.empty()) { 670 Unschedule(stream_info);
469 Node* current_node = queue.front(); 671 return stream_info->id;
470 const Node* parent_node = current_node->parent; 672 }
471 if (current_node->blocked || !current_node->ready) { 673 }
472 if (current_node->total_writeable_child_weights > 0) { 674 LOG(DFATAL) << "No usable streams";
473 // This node isn't writeable, but it has writeable children. 675 return kHttp2RootStreamId;
474 // Calculate the total fraction of resources we can allot 676 }
475 // to this subtree. 677
476 current_node->priority = parent_node->priority * 678 template <typename StreamIdType>
477 (static_cast<float>(current_node->weight) / 679 bool Http2PriorityWriteScheduler<StreamIdType>::ValidateInvariantsForTests()
478 static_cast<float>(parent_node->total_writeable_child_weights)); 680 const {
479 // Examine the children. 681 int total_streams = 0;
480 for (Node* child : current_node->children) { 682 int streams_visited = 0;
481 queue.push_back(child); 683 // Iterate through all streams in the map.
482 } 684 for (const auto& kv : all_stream_infos_) {
483 } else { 685 ++total_streams;
484 // There's nothing to see in this subtree. 686 ++streams_visited;
485 current_node->priority = 0; 687 const StreamInfo& stream_info = *kv.second;
486 } 688 // All streams except the root should have a parent, and should appear in
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));
495 }
496 // Remove this node from the queue.
497 queue.pop_front();
498 }
499
500 // Sort the nodes in descending order of priority.
501 std::sort(priority_list.begin(), priority_list.end(),
502 NodePriorityComparator());
503
504 return priority_list;
505 }
506
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. 689 // the children of that parent.
518 if (node.id != kRootNodeId && !HasChild(node.parent->id, node.id)) { 690 if (stream_info.id != kHttp2RootStreamId &&
519 DLOG(INFO) << "Parent node " << node.parent->id 691 !StreamHasChild(stream_info.parent->id, stream_info.id)) {
520 << " does not exist, or does not list node " << node.id 692 DLOG(INFO) << "Parent stream " << stream_info.parent->id
521 << " as its child."; 693 << " is not registered, or does not list stream "
694 << stream_info.id << " as its child.";
522 return false; 695 return false;
523 } 696 }
524 697
525 if (!node.children.empty()) { 698 if (!stream_info.children.empty()) {
526 int total_child_weights = 0; 699 int total_child_weights = 0;
527 // Iterate through the node's children. 700 // Iterate through the stream's children.
528 for (Node* child : node.children) { 701 for (StreamInfo* child : stream_info.children) {
529 ++nodes_visited; 702 ++streams_visited;
530 // Each node in the list should exist and should have this node 703 // Each stream in the list should exist and should have this stream
531 // set as its parent. 704 // set as its parent.
532 if (!NodeExists(child->id) || node.id != GetParent(child->id)) { 705 if (!StreamRegistered(child->id) ||
533 DLOG(INFO) << "Child node " << child->id << " does not exist, " 706 stream_info.id != GetStreamParent(child->id)) {
534 << "or does not list " << node.id << " as its parent."; 707 DLOG(INFO) << "Child stream " << child->id << " is not registered, "
708 << "or does not list " << stream_info.id
709 << " as its parent.";
535 return false; 710 return false;
536 } 711 }
537 total_child_weights += child->weight; 712 total_child_weights += child->weight;
538 } 713 }
539 // Verify that total_child_weights is correct. 714 // Verify that total_child_weights is correct.
540 if (total_child_weights != node.total_child_weights) { 715 if (total_child_weights != stream_info.total_child_weights) {
541 DLOG(INFO) << "Child weight totals do not agree. For node " << node.id 716 DLOG(INFO) << "Child weight totals do not agree. For stream "
542 << " total_child_weights has value " 717 << stream_info.id << " total_child_weights has value "
543 << node.total_child_weights 718 << stream_info.total_child_weights << ", expected "
544 << ", expected " << total_child_weights; 719 << total_child_weights;
545 return false; 720 return false;
546 } 721 }
547 } 722 }
548 } 723 }
549 724
550 // Make sure num_nodes reflects the total number of nodes the map contains. 725 // Make sure num_streams reflects the total number of streams the map
551 if (total_nodes != num_nodes()) { 726 // contains.
552 DLOG(INFO) << "Map contains incorrect number of nodes."; 727 if (total_streams != num_streams()) {
728 DLOG(INFO) << "Map contains incorrect number of streams.";
553 return false; 729 return false;
554 } 730 }
555 // Validate the validation function; we should have visited each node twice 731 // Validate the validation function; we should have visited each stream twice
556 // (except for the root) 732 // (except for the root)
557 DCHECK(nodes_visited == 2*num_nodes() - 1); 733 DCHECK(streams_visited == 2 * num_streams() - 1);
558 return true; 734 return true;
559 } 735 }
560 736
561 } // namespace net 737 } // namespace net
562 738
563 #endif // NET_SPDY_HTTP2_WRITE_SCHEDULER_H_ 739 #endif // NET_SPDY_HTTP2_WRITE_SCHEDULER_H_
OLDNEW
« no previous file with comments | « no previous file | net/spdy/http2_write_scheduler_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698