1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "net/spdy/http2_priority_dependencies.h"
11
12 #include "base/not_fatal_until.h"
13 #include "base/trace_event/memory_usage_estimator.h"
14
15 namespace net {
16
17 Http2PriorityDependencies::Http2PriorityDependencies() = default;
18
19 Http2PriorityDependencies::~Http2PriorityDependencies() = default;
20
OnStreamCreation(spdy::SpdyStreamId id,spdy::SpdyPriority priority,spdy::SpdyStreamId * parent_stream_id,int * weight,bool * exclusive)21 void Http2PriorityDependencies::OnStreamCreation(
22 spdy::SpdyStreamId id,
23 spdy::SpdyPriority priority,
24 spdy::SpdyStreamId* parent_stream_id,
25 int* weight,
26 bool* exclusive) {
27 if (entry_by_stream_id_.find(id) != entry_by_stream_id_.end())
28 return;
29
30 *parent_stream_id = 0;
31 *exclusive = true;
32 // Since the generated dependency graph is a single linked list, the value
33 // of weight should not actually matter, and perhaps the default weight of 16
34 // from the HTTP/2 spec would be reasonable. However, there are some servers
35 // which currently interpret the weight field like an old SPDY priority value.
36 // As long as those servers need to be supported, weight should be set to
37 // a value those servers will interpret correctly.
38 *weight = spdy::Spdy3PriorityToHttp2Weight(priority);
39
40 // Dependent on the lowest-priority stream that has a priority >= |priority|.
41 IdList::iterator parent;
42 if (PriorityLowerBound(priority, &parent)) {
43 *parent_stream_id = parent->first;
44 }
45
46 id_priority_lists_[priority].emplace_back(id, priority);
47 auto it = id_priority_lists_[priority].end();
48 --it;
49 entry_by_stream_id_[id] = it;
50 }
51
PriorityLowerBound(spdy::SpdyPriority priority,IdList::iterator * bound)52 bool Http2PriorityDependencies::PriorityLowerBound(spdy::SpdyPriority priority,
53 IdList::iterator* bound) {
54 for (int i = priority; i >= spdy::kV3HighestPriority; --i) {
55 if (!id_priority_lists_[i].empty()) {
56 *bound = id_priority_lists_[i].end();
57 --(*bound);
58 return true;
59 }
60 }
61 return false;
62 }
63
ParentOfStream(spdy::SpdyStreamId id,IdList::iterator * parent)64 bool Http2PriorityDependencies::ParentOfStream(spdy::SpdyStreamId id,
65 IdList::iterator* parent) {
66 auto entry = entry_by_stream_id_.find(id);
67 CHECK(entry != entry_by_stream_id_.end(), base::NotFatalUntil::M130);
68
69 spdy::SpdyPriority priority = entry->second->second;
70 auto curr = entry->second;
71 if (curr != id_priority_lists_[priority].begin()) {
72 *parent = curr;
73 --(*parent);
74 return true;
75 }
76
77 // |id| is at the head of its priority list, so its parent is the last
78 // entry of the next-highest priority band.
79 if (priority == spdy::kV3HighestPriority) {
80 return false;
81 }
82 return PriorityLowerBound(priority - 1, parent);
83 }
84
ChildOfStream(spdy::SpdyStreamId id,IdList::iterator * child)85 bool Http2PriorityDependencies::ChildOfStream(spdy::SpdyStreamId id,
86 IdList::iterator* child) {
87 auto entry = entry_by_stream_id_.find(id);
88 CHECK(entry != entry_by_stream_id_.end(), base::NotFatalUntil::M130);
89
90 spdy::SpdyPriority priority = entry->second->second;
91 *child = entry->second;
92 ++(*child);
93 if (*child != id_priority_lists_[priority].end()) {
94 return true;
95 }
96
97 // |id| is at the end of its priority list, so its child is the stream
98 // at the front of the next-lowest priority band.
99 for (int i = priority + 1; i <= spdy::kV3LowestPriority; ++i) {
100 if (!id_priority_lists_[i].empty()) {
101 *child = id_priority_lists_[i].begin();
102 return true;
103 }
104 }
105
106 return false;
107 }
108
109 std::vector<Http2PriorityDependencies::DependencyUpdate>
OnStreamUpdate(spdy::SpdyStreamId id,spdy::SpdyPriority new_priority)110 Http2PriorityDependencies::OnStreamUpdate(spdy::SpdyStreamId id,
111 spdy::SpdyPriority new_priority) {
112 std::vector<DependencyUpdate> result;
113 result.reserve(2);
114
115 auto curr_entry = entry_by_stream_id_.find(id);
116 if (curr_entry == entry_by_stream_id_.end()) {
117 return result;
118 }
119
120 spdy::SpdyPriority old_priority = curr_entry->second->second;
121 if (old_priority == new_priority) {
122 return result;
123 }
124
125 IdList::iterator old_parent;
126 bool old_has_parent = ParentOfStream(id, &old_parent);
127
128 IdList::iterator new_parent;
129 bool new_has_parent = PriorityLowerBound(new_priority, &new_parent);
130
131 // If we move |id| from MEDIUM to LOW, where HIGH = {other_id}, MEDIUM = {id},
132 // and LOW = {}, then PriorityLowerBound(new_priority) is |id|. In this corner
133 // case, |id| does not change parents.
134 if (new_has_parent && new_parent->first == id) {
135 new_has_parent = old_has_parent;
136 new_parent = old_parent;
137 }
138
139 // If the parent has changed, we generate dependency updates.
140 if ((old_has_parent != new_has_parent) ||
141 (old_has_parent && old_parent->first != new_parent->first)) {
142 // If |id| has a child, then that child moves to be dependent on
143 // |old_parent|.
144 IdList::iterator old_child;
145 if (ChildOfStream(id, &old_child)) {
146 int weight = spdy::Spdy3PriorityToHttp2Weight(old_child->second);
147 if (old_has_parent) {
148 result.push_back({old_child->first, old_parent->first, weight, true});
149 } else {
150 result.push_back({old_child->first, 0, weight, true});
151 }
152 }
153
154 int weight = spdy::Spdy3PriorityToHttp2Weight(new_priority);
155 // |id| moves to be dependent on |new_parent|.
156 if (new_has_parent) {
157 result.push_back({id, new_parent->first, weight, true});
158 } else {
159 result.push_back({id, 0, weight, true});
160 }
161 }
162
163 // Move to the new priority.
164 auto old = entry_by_stream_id_.find(id);
165 id_priority_lists_[old->second->second].erase(old->second);
166 id_priority_lists_[new_priority].emplace_back(id, new_priority);
167 auto it = id_priority_lists_[new_priority].end();
168 --it;
169 entry_by_stream_id_[id] = it;
170
171 return result;
172 }
173
OnStreamDestruction(spdy::SpdyStreamId id)174 void Http2PriorityDependencies::OnStreamDestruction(spdy::SpdyStreamId id) {
175 auto emit = entry_by_stream_id_.find(id);
176 if (emit == entry_by_stream_id_.end())
177 return;
178
179 auto it = emit->second;
180 id_priority_lists_[it->second].erase(it);
181 entry_by_stream_id_.erase(emit);
182 }
183
184 } // namespace net
185