OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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> | 8 #include <stdint.h> |
9 | 9 |
10 #include <algorithm> | 10 #include <algorithm> |
11 #include <cmath> | 11 #include <cmath> |
12 #include <deque> | 12 #include <deque> |
13 #include <map> | 13 #include <map> |
14 #include <memory> | 14 #include <memory> |
15 #include <queue> | 15 #include <queue> |
16 #include <set> | 16 #include <set> |
17 #include <tuple> | 17 #include <tuple> |
18 #include <unordered_map> | 18 #include <unordered_map> |
19 #include <utility> | 19 #include <utility> |
20 #include <vector> | 20 #include <vector> |
21 | 21 |
22 #include "base/containers/linked_list.h" | 22 #include "base/containers/linked_list.h" |
23 #include "base/logging.h" | 23 #include "base/logging.h" |
24 #include "base/macros.h" | 24 #include "base/macros.h" |
25 #include "base/stl_util.h" | 25 #include "base/memory/ptr_util.h" |
26 #include "net/spdy/spdy_bug_tracker.h" | 26 #include "net/spdy/spdy_bug_tracker.h" |
27 #include "net/spdy/spdy_protocol.h" | 27 #include "net/spdy/spdy_protocol.h" |
28 #include "net/spdy/write_scheduler.h" | 28 #include "net/spdy/write_scheduler.h" |
29 | 29 |
30 namespace net { | 30 namespace net { |
31 | 31 |
32 namespace test { | 32 namespace test { |
33 template <typename StreamIdType> | 33 template <typename StreamIdType> |
34 class Http2PriorityWriteSchedulerPeer; | 34 class Http2PriorityWriteSchedulerPeer; |
35 } | 35 } |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 size_t NumReadyStreams() const override; | 74 size_t NumReadyStreams() const override; |
75 | 75 |
76 // Return the number of streams currently in the tree. | 76 // Return the number of streams currently in the tree. |
77 int num_streams() const; | 77 int num_streams() const; |
78 | 78 |
79 private: | 79 private: |
80 friend class test::Http2PriorityWriteSchedulerPeer<StreamIdType>; | 80 friend class test::Http2PriorityWriteSchedulerPeer<StreamIdType>; |
81 | 81 |
82 struct StreamInfo; | 82 struct StreamInfo; |
83 using StreamInfoVector = std::vector<StreamInfo*>; | 83 using StreamInfoVector = std::vector<StreamInfo*>; |
84 using StreamInfoMap = std::unordered_map<StreamIdType, StreamInfo*>; | |
85 | 84 |
86 struct StreamInfo : public base::LinkNode<StreamInfo> { | 85 struct StreamInfo : public base::LinkNode<StreamInfo> { |
87 // ID for this stream. | 86 // ID for this stream. |
88 StreamIdType id; | 87 StreamIdType id; |
89 // StreamInfo for parent stream. | 88 // StreamInfo for parent stream. |
90 StreamInfo* parent = nullptr; | 89 StreamInfo* parent = nullptr; |
91 // Weights can range between 1 and 256 (inclusive). | 90 // Weights can range between 1 and 256 (inclusive). |
92 int weight = kHttp2DefaultStreamWeight; | 91 int weight = kHttp2DefaultStreamWeight; |
93 // The total weight of this stream's direct descendants. | 92 // The total weight of this stream's direct descendants. |
94 int total_child_weights = 0; | 93 int total_child_weights = 0; |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
160 // Unless there are bugs, this should always return true. | 159 // Unless there are bugs, this should always return true. |
161 bool ValidateInvariantsForTests() const; | 160 bool ValidateInvariantsForTests() const; |
162 | 161 |
163 // Returns true if the parent stream has the given stream in its children. | 162 // Returns true if the parent stream has the given stream in its children. |
164 bool StreamHasChild(const StreamInfo& parent_info, | 163 bool StreamHasChild(const StreamInfo& parent_info, |
165 const StreamInfo* child_info) const; | 164 const StreamInfo* child_info) const; |
166 | 165 |
167 // Pointee owned by all_stream_infos_. | 166 // Pointee owned by all_stream_infos_. |
168 StreamInfo* root_stream_info_; | 167 StreamInfo* root_stream_info_; |
169 // Maps from stream IDs to StreamInfo objects. | 168 // Maps from stream IDs to StreamInfo objects. |
170 StreamInfoMap all_stream_infos_; | 169 std::unordered_map<StreamIdType, std::unique_ptr<StreamInfo>> |
171 base::STLValueDeleter<StreamInfoMap> all_stream_infos_deleter_; | 170 all_stream_infos_; |
172 // Queue containing all ready streams, ordered with streams of higher | 171 // Queue containing all ready streams, ordered with streams of higher |
173 // priority before streams of lower priority, and, among streams of equal | 172 // priority before streams of lower priority, and, among streams of equal |
174 // priority, streams with lower ordinal before those with higher | 173 // priority, streams with lower ordinal before those with higher |
175 // ordinal. Note that not all streams in scheduling_queue_ are eligible to be | 174 // ordinal. Note that not all streams in scheduling_queue_ are eligible to be |
176 // picked as the next stream: some may have ancestor stream(s) that are ready | 175 // picked as the next stream: some may have ancestor stream(s) that are ready |
177 // and unblocked. In these situations the occluded child streams are left in | 176 // and unblocked. In these situations the occluded child streams are left in |
178 // the queue, to reduce churn. | 177 // the queue, to reduce churn. |
179 base::LinkedList<StreamInfo> scheduling_queue_; | 178 base::LinkedList<StreamInfo> scheduling_queue_; |
180 // Ordinal value to assign to next node inserted into scheduling_queue_ when | 179 // Ordinal value to assign to next node inserted into scheduling_queue_ when |
181 // |add_to_front == true|. Decremented after each assignment. | 180 // |add_to_front == true|. Decremented after each assignment. |
182 int64_t head_ordinal_ = -1; | 181 int64_t head_ordinal_ = -1; |
183 // Ordinal value to assign to next node inserted into scheduling_queue_ when | 182 // Ordinal value to assign to next node inserted into scheduling_queue_ when |
184 // |add_to_front == false|. Incremented after each assignment. | 183 // |add_to_front == false|. Incremented after each assignment. |
185 int64_t tail_ordinal_ = 0; | 184 int64_t tail_ordinal_ = 0; |
186 | 185 |
187 DISALLOW_COPY_AND_ASSIGN(Http2PriorityWriteScheduler); | 186 DISALLOW_COPY_AND_ASSIGN(Http2PriorityWriteScheduler); |
188 }; | 187 }; |
189 | 188 |
190 template <typename StreamIdType> | 189 template <typename StreamIdType> |
191 Http2PriorityWriteScheduler<StreamIdType>::Http2PriorityWriteScheduler() | 190 Http2PriorityWriteScheduler<StreamIdType>::Http2PriorityWriteScheduler() { |
192 : all_stream_infos_deleter_(&all_stream_infos_) { | 191 std::unique_ptr<StreamInfo> root_stream_info = base::MakeUnique<StreamInfo>(); |
193 root_stream_info_ = new StreamInfo(); | 192 root_stream_info_ = root_stream_info.get(); |
194 root_stream_info_->id = kHttp2RootStreamId; | 193 root_stream_info->id = kHttp2RootStreamId; |
195 root_stream_info_->weight = kHttp2DefaultStreamWeight; | 194 root_stream_info->weight = kHttp2DefaultStreamWeight; |
196 root_stream_info_->parent = nullptr; | 195 root_stream_info->parent = nullptr; |
197 root_stream_info_->priority = 1.0; | 196 root_stream_info->priority = 1.0; |
198 root_stream_info_->ready = false; | 197 root_stream_info->ready = false; |
199 all_stream_infos_[kHttp2RootStreamId] = root_stream_info_; | 198 all_stream_infos_[kHttp2RootStreamId] = std::move(root_stream_info); |
200 } | 199 } |
201 | 200 |
202 template <typename StreamIdType> | 201 template <typename StreamIdType> |
203 int Http2PriorityWriteScheduler<StreamIdType>::num_streams() const { | 202 int Http2PriorityWriteScheduler<StreamIdType>::num_streams() const { |
204 return all_stream_infos_.size(); | 203 return all_stream_infos_.size(); |
205 } | 204 } |
206 | 205 |
207 template <typename StreamIdType> | 206 template <typename StreamIdType> |
208 bool Http2PriorityWriteScheduler<StreamIdType>::StreamRegistered( | 207 bool Http2PriorityWriteScheduler<StreamIdType>::StreamRegistered( |
209 StreamIdType stream_id) const { | 208 StreamIdType stream_id) const { |
(...skipping 12 matching lines...) Expand all Loading... |
222 return; | 221 return; |
223 } | 222 } |
224 | 223 |
225 StreamInfo* parent = FindStream(precedence.parent_id()); | 224 StreamInfo* parent = FindStream(precedence.parent_id()); |
226 if (parent == nullptr) { | 225 if (parent == nullptr) { |
227 // parent_id may legitimately not be registered yet--see b/15676312. | 226 // parent_id may legitimately not be registered yet--see b/15676312. |
228 DVLOG(1) << "Parent stream " << precedence.parent_id() << " not registered"; | 227 DVLOG(1) << "Parent stream " << precedence.parent_id() << " not registered"; |
229 parent = root_stream_info_; | 228 parent = root_stream_info_; |
230 } | 229 } |
231 | 230 |
232 StreamInfo* new_stream_info = new StreamInfo; | 231 std::unique_ptr<StreamInfo> new_stream_info = base::MakeUnique<StreamInfo>(); |
233 new_stream_info->id = stream_id; | 232 StreamInfo* new_stream_info_ptr = new_stream_info.get(); |
234 new_stream_info->weight = precedence.weight(); | 233 new_stream_info_ptr->id = stream_id; |
235 new_stream_info->parent = parent; | 234 new_stream_info_ptr->weight = precedence.weight(); |
236 all_stream_infos_[stream_id] = new_stream_info; | 235 new_stream_info_ptr->parent = parent; |
| 236 all_stream_infos_[stream_id] = std::move(new_stream_info); |
237 if (precedence.is_exclusive()) { | 237 if (precedence.is_exclusive()) { |
238 // Move the parent's current children below the new stream. | 238 // Move the parent's current children below the new stream. |
239 using std::swap; | 239 using std::swap; |
240 swap(new_stream_info->children, parent->children); | 240 swap(new_stream_info_ptr->children, parent->children); |
241 new_stream_info->total_child_weights = parent->total_child_weights; | 241 new_stream_info_ptr->total_child_weights = parent->total_child_weights; |
242 // Update each child's parent. | 242 // Update each child's parent. |
243 for (StreamInfo* child : new_stream_info->children) { | 243 for (StreamInfo* child : new_stream_info_ptr->children) { |
244 child->parent = new_stream_info; | 244 child->parent = new_stream_info_ptr; |
245 } | 245 } |
246 // Clear parent's old child data. | 246 // Clear parent's old child data. |
247 DCHECK(parent->children.empty()); | 247 DCHECK(parent->children.empty()); |
248 parent->total_child_weights = 0; | 248 parent->total_child_weights = 0; |
249 } | 249 } |
250 // Add new stream to parent. | 250 // Add new stream to parent. |
251 parent->children.push_back(new_stream_info); | 251 parent->children.push_back(new_stream_info_ptr); |
252 parent->total_child_weights += precedence.weight(); | 252 parent->total_child_weights += precedence.weight(); |
253 | 253 |
254 // Update all priorities under parent, since addition of a stream affects | 254 // Update all priorities under parent, since addition of a stream affects |
255 // sibling priorities as well. | 255 // sibling priorities as well. |
256 UpdatePrioritiesUnder(parent); | 256 UpdatePrioritiesUnder(parent); |
257 | 257 |
258 // Stream starts with ready == false, so no need to schedule it yet. | 258 // Stream starts with ready == false, so no need to schedule it yet. |
259 DCHECK(!new_stream_info->ready); | 259 DCHECK(!new_stream_info_ptr->ready); |
260 } | 260 } |
261 | 261 |
262 template <typename StreamIdType> | 262 template <typename StreamIdType> |
263 void Http2PriorityWriteScheduler<StreamIdType>::UnregisterStream( | 263 void Http2PriorityWriteScheduler<StreamIdType>::UnregisterStream( |
264 StreamIdType stream_id) { | 264 StreamIdType stream_id) { |
265 if (stream_id == kHttp2RootStreamId) { | 265 if (stream_id == kHttp2RootStreamId) { |
266 SPDY_BUG << "Cannot unregister root stream"; | 266 SPDY_BUG << "Cannot unregister root stream"; |
267 return; | 267 return; |
268 } | 268 } |
269 // Remove the stream from table. | 269 // Remove the stream from table. |
270 typename StreamInfoMap::iterator it = all_stream_infos_.find(stream_id); | 270 auto it = all_stream_infos_.find(stream_id); |
271 if (it == all_stream_infos_.end()) { | 271 if (it == all_stream_infos_.end()) { |
272 SPDY_BUG << "Stream " << stream_id << " not registered"; | 272 SPDY_BUG << "Stream " << stream_id << " not registered"; |
273 return; | 273 return; |
274 } | 274 } |
275 std::unique_ptr<StreamInfo> stream_info(std::move(it->second)); | 275 std::unique_ptr<StreamInfo> stream_info(std::move(it->second)); |
276 all_stream_infos_.erase(it); | 276 all_stream_infos_.erase(it); |
277 // If ready (and hence scheduled), unschedule. | 277 // If ready (and hence scheduled), unschedule. |
278 if (stream_info->ready) { | 278 if (stream_info->ready) { |
279 Unschedule(stream_info.get()); | 279 Unschedule(stream_info.get()); |
280 } | 280 } |
(...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
568 return true; | 568 return true; |
569 } | 569 } |
570 } | 570 } |
571 return false; | 571 return false; |
572 } | 572 } |
573 | 573 |
574 template <typename StreamIdType> | 574 template <typename StreamIdType> |
575 const typename Http2PriorityWriteScheduler<StreamIdType>::StreamInfo* | 575 const typename Http2PriorityWriteScheduler<StreamIdType>::StreamInfo* |
576 Http2PriorityWriteScheduler<StreamIdType>::FindStream( | 576 Http2PriorityWriteScheduler<StreamIdType>::FindStream( |
577 StreamIdType stream_id) const { | 577 StreamIdType stream_id) const { |
578 typename StreamInfoMap::const_iterator it = all_stream_infos_.find(stream_id); | 578 auto it = all_stream_infos_.find(stream_id); |
579 return it == all_stream_infos_.end() ? nullptr : it->second; | 579 return it == all_stream_infos_.end() ? nullptr : it->second.get(); |
580 } | 580 } |
581 | 581 |
582 template <typename StreamIdType> | 582 template <typename StreamIdType> |
583 typename Http2PriorityWriteScheduler<StreamIdType>::StreamInfo* | 583 typename Http2PriorityWriteScheduler<StreamIdType>::StreamInfo* |
584 Http2PriorityWriteScheduler<StreamIdType>::FindStream(StreamIdType stream_id) { | 584 Http2PriorityWriteScheduler<StreamIdType>::FindStream(StreamIdType stream_id) { |
585 typename StreamInfoMap::iterator it = all_stream_infos_.find(stream_id); | 585 auto it = all_stream_infos_.find(stream_id); |
586 return it == all_stream_infos_.end() ? nullptr : it->second; | 586 return it == all_stream_infos_.end() ? nullptr : it->second.get(); |
587 } | 587 } |
588 | 588 |
589 template <typename StreamIdType> | 589 template <typename StreamIdType> |
590 void Http2PriorityWriteScheduler<StreamIdType>::UpdatePrioritiesUnder( | 590 void Http2PriorityWriteScheduler<StreamIdType>::UpdatePrioritiesUnder( |
591 StreamInfo* stream_info) { | 591 StreamInfo* stream_info) { |
592 for (StreamInfo* child : stream_info->children) { | 592 for (StreamInfo* child : stream_info->children) { |
593 child->priority = stream_info->priority * | 593 child->priority = stream_info->priority * |
594 (static_cast<float>(child->weight) / | 594 (static_cast<float>(child->weight) / |
595 static_cast<float>(stream_info->total_child_weights)); | 595 static_cast<float>(stream_info->total_child_weights)); |
596 if (child->ready) { | 596 if (child->ready) { |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
680 template <typename StreamIdType> | 680 template <typename StreamIdType> |
681 bool Http2PriorityWriteScheduler<StreamIdType>::ValidateInvariantsForTests() | 681 bool Http2PriorityWriteScheduler<StreamIdType>::ValidateInvariantsForTests() |
682 const { | 682 const { |
683 int total_streams = 0; | 683 int total_streams = 0; |
684 int streams_visited = 0; | 684 int streams_visited = 0; |
685 // Iterate through all streams in the map. | 685 // Iterate through all streams in the map. |
686 for (const auto& kv : all_stream_infos_) { | 686 for (const auto& kv : all_stream_infos_) { |
687 ++total_streams; | 687 ++total_streams; |
688 ++streams_visited; | 688 ++streams_visited; |
689 StreamIdType stream_id = kv.first; | 689 StreamIdType stream_id = kv.first; |
690 const StreamInfo& stream_info = *kv.second; | 690 const StreamInfo& stream_info = *kv.second.get(); |
691 | 691 |
692 // Verify each StreamInfo mapped under the proper stream ID. | 692 // Verify each StreamInfo mapped under the proper stream ID. |
693 if (stream_id != stream_info.id) { | 693 if (stream_id != stream_info.id) { |
694 DLOG(INFO) << "Stream ID " << stream_id << " maps to StreamInfo with ID " | 694 DLOG(INFO) << "Stream ID " << stream_id << " maps to StreamInfo with ID " |
695 << stream_info.id; | 695 << stream_info.id; |
696 return false; | 696 return false; |
697 } | 697 } |
698 | 698 |
699 // All streams except the root should have a parent, and should appear in | 699 // All streams except the root should have a parent, and should appear in |
700 // the children of that parent. | 700 // the children of that parent. |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
740 } | 740 } |
741 // Validate the validation function; we should have visited each stream twice | 741 // Validate the validation function; we should have visited each stream twice |
742 // (except for the root) | 742 // (except for the root) |
743 DCHECK(streams_visited == 2 * num_streams() - 1); | 743 DCHECK(streams_visited == 2 * num_streams() - 1); |
744 return true; | 744 return true; |
745 } | 745 } |
746 | 746 |
747 } // namespace net | 747 } // namespace net |
748 | 748 |
749 #endif // NET_SPDY_HTTP2_WRITE_SCHEDULER_H_ | 749 #endif // NET_SPDY_HTTP2_WRITE_SCHEDULER_H_ |
OLD | NEW |