• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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