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