OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #ifndef NET_SPDY_PRIORITY_WRITE_SCHEDULER_H_ | |
6 #define NET_SPDY_PRIORITY_WRITE_SCHEDULER_H_ | |
7 | |
8 #include <stddef.h> | |
9 #include <stdint.h> | |
10 | |
11 #include <algorithm> | |
12 #include <deque> | |
13 #include <tuple> | |
14 #include <unordered_map> | |
15 #include <utility> | |
16 #include <vector> | |
17 | |
18 #include "base/logging.h" | |
19 #include "net/spdy/spdy_bug_tracker.h" | |
20 #include "net/spdy/spdy_protocol.h" | |
21 #include "net/spdy/write_scheduler.h" | |
22 | |
23 namespace net { | |
24 | |
25 namespace test { | |
26 template <typename StreamIdType> | |
27 class PriorityWriteSchedulerPeer; | |
28 } | |
29 | |
30 // WriteScheduler implementation that manages the order in which streams are | |
31 // written using the SPDY priority scheme described at: | |
32 // https://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1#TOC-2.3.3-
Stream-priority | |
33 // | |
34 // Internally, PriorityWriteScheduler consists of 8 PriorityInfo objects, one | |
35 // for each priority value. Each PriorityInfo contains a list of streams of | |
36 // that priority that are ready to write, as well as a timestamp of the last | |
37 // I/O event that occurred for a stream of that priority. | |
38 template <typename StreamIdType> | |
39 class PriorityWriteScheduler : public WriteScheduler<StreamIdType> { | |
40 public: | |
41 using typename WriteScheduler<StreamIdType>::StreamPrecedenceType; | |
42 | |
43 // Creates scheduler with no streams. | |
44 PriorityWriteScheduler() = default; | |
45 | |
46 void RegisterStream(StreamIdType stream_id, | |
47 const StreamPrecedenceType& precedence) override { | |
48 SPDY_BUG_IF(!precedence.is_spdy3_priority()) << "Expected SPDY priority"; | |
49 | |
50 // parent_id not used here, but may as well validate it. However, | |
51 // parent_id may legitimately not be registered yet--see b/15676312. | |
52 StreamIdType parent_id = precedence.parent_id(); | |
53 DVLOG_IF(1, parent_id != kHttp2RootStreamId && !StreamRegistered(parent_id)) | |
54 << "Parent stream " << parent_id << " not registered"; | |
55 | |
56 if (stream_id == kHttp2RootStreamId) { | |
57 SPDY_BUG << "Stream " << kHttp2RootStreamId << " already registered"; | |
58 return; | |
59 } | |
60 StreamInfo stream_info = {precedence.spdy3_priority(), stream_id, false}; | |
61 bool inserted = | |
62 stream_infos_.insert(std::make_pair(stream_id, stream_info)).second; | |
63 SPDY_BUG_IF(!inserted) << "Stream " << stream_id << " already registered"; | |
64 } | |
65 | |
66 void UnregisterStream(StreamIdType stream_id) override { | |
67 auto it = stream_infos_.find(stream_id); | |
68 if (it == stream_infos_.end()) { | |
69 SPDY_BUG << "Stream " << stream_id << " not registered"; | |
70 return; | |
71 } | |
72 StreamInfo& stream_info = it->second; | |
73 if (stream_info.ready) { | |
74 bool erased = | |
75 Erase(&priority_infos_[stream_info.priority].ready_list, stream_info); | |
76 DCHECK(erased); | |
77 } | |
78 stream_infos_.erase(it); | |
79 } | |
80 | |
81 bool StreamRegistered(StreamIdType stream_id) const override { | |
82 return stream_infos_.find(stream_id) != stream_infos_.end(); | |
83 } | |
84 | |
85 StreamPrecedenceType GetStreamPrecedence( | |
86 StreamIdType stream_id) const override { | |
87 auto it = stream_infos_.find(stream_id); | |
88 if (it == stream_infos_.end()) { | |
89 DVLOG(1) << "Stream " << stream_id << " not registered"; | |
90 return StreamPrecedenceType(kV3LowestPriority); | |
91 } | |
92 return StreamPrecedenceType(it->second.priority); | |
93 } | |
94 | |
95 void UpdateStreamPrecedence(StreamIdType stream_id, | |
96 const StreamPrecedenceType& precedence) override { | |
97 SPDY_BUG_IF(!precedence.is_spdy3_priority()) << "Expected SPDY priority"; | |
98 | |
99 // parent_id not used here, but may as well validate it. However, | |
100 // parent_id may legitimately not be registered yet--see b/15676312. | |
101 StreamIdType parent_id = precedence.parent_id(); | |
102 DVLOG_IF(1, parent_id != kHttp2RootStreamId && !StreamRegistered(parent_id)) | |
103 << "Parent stream " << parent_id << " not registered"; | |
104 | |
105 auto it = stream_infos_.find(stream_id); | |
106 if (it == stream_infos_.end()) { | |
107 // TODO(mpw): add to stream_infos_ on demand--see b/15676312. | |
108 DVLOG(1) << "Stream " << stream_id << " not registered"; | |
109 return; | |
110 } | |
111 StreamInfo& stream_info = it->second; | |
112 SpdyPriority new_priority = precedence.spdy3_priority(); | |
113 if (stream_info.priority == new_priority) { | |
114 return; | |
115 } | |
116 if (stream_info.ready) { | |
117 bool erased = | |
118 Erase(&priority_infos_[stream_info.priority].ready_list, stream_info); | |
119 DCHECK(erased); | |
120 priority_infos_[new_priority].ready_list.push_back(&stream_info); | |
121 ++num_ready_streams_; | |
122 } | |
123 stream_info.priority = new_priority; | |
124 } | |
125 | |
126 std::vector<StreamIdType> GetStreamChildren( | |
127 StreamIdType stream_id) const override { | |
128 return std::vector<StreamIdType>(); | |
129 } | |
130 | |
131 void RecordStreamEventTime(StreamIdType stream_id, | |
132 int64_t now_in_usec) override { | |
133 auto it = stream_infos_.find(stream_id); | |
134 if (it == stream_infos_.end()) { | |
135 SPDY_BUG << "Stream " << stream_id << " not registered"; | |
136 return; | |
137 } | |
138 PriorityInfo& priority_info = priority_infos_[it->second.priority]; | |
139 priority_info.last_event_time_usec = | |
140 std::max(priority_info.last_event_time_usec, now_in_usec); | |
141 } | |
142 | |
143 int64_t GetLatestEventWithPrecedence(StreamIdType stream_id) const override { | |
144 auto it = stream_infos_.find(stream_id); | |
145 if (it == stream_infos_.end()) { | |
146 SPDY_BUG << "Stream " << stream_id << " not registered"; | |
147 return 0; | |
148 } | |
149 int64_t last_event_time_usec = 0; | |
150 const StreamInfo& stream_info = it->second; | |
151 for (SpdyPriority p = kV3HighestPriority; p < stream_info.priority; ++p) { | |
152 last_event_time_usec = std::max(last_event_time_usec, | |
153 priority_infos_[p].last_event_time_usec); | |
154 } | |
155 return last_event_time_usec; | |
156 } | |
157 | |
158 StreamIdType PopNextReadyStream() override { | |
159 return std::get<0>(PopNextReadyStreamAndPrecedence()); | |
160 } | |
161 | |
162 // Returns the next ready stream and its precedence. | |
163 std::tuple<StreamIdType, StreamPrecedenceType> | |
164 PopNextReadyStreamAndPrecedence() override { | |
165 for (SpdyPriority p = kV3HighestPriority; p <= kV3LowestPriority; ++p) { | |
166 ReadyList& ready_list = priority_infos_[p].ready_list; | |
167 if (!ready_list.empty()) { | |
168 StreamInfo* info = ready_list.front(); | |
169 ready_list.pop_front(); | |
170 --num_ready_streams_; | |
171 | |
172 DCHECK(stream_infos_.find(info->stream_id) != stream_infos_.end()); | |
173 info->ready = false; | |
174 return std::make_tuple(info->stream_id, | |
175 StreamPrecedenceType(info->priority)); | |
176 } | |
177 } | |
178 SPDY_BUG << "No ready streams available"; | |
179 return std::make_tuple(0, StreamPrecedenceType(kV3LowestPriority)); | |
180 } | |
181 | |
182 bool ShouldYield(StreamIdType stream_id) const override { | |
183 auto it = stream_infos_.find(stream_id); | |
184 if (it == stream_infos_.end()) { | |
185 SPDY_BUG << "Stream " << stream_id << " not registered"; | |
186 return false; | |
187 } | |
188 | |
189 // If there's a higher priority stream, this stream should yield. | |
190 const StreamInfo& stream_info = it->second; | |
191 for (SpdyPriority p = kV3HighestPriority; p < stream_info.priority; ++p) { | |
192 if (!priority_infos_[p].ready_list.empty()) { | |
193 return true; | |
194 } | |
195 } | |
196 | |
197 // If this priority level is empty, or this stream is the next up, there's | |
198 // no need to yield. | |
199 const auto& ready_list = priority_infos_[it->second.priority].ready_list; | |
200 if (ready_list.empty() || ready_list.front()->stream_id == stream_id) { | |
201 return false; | |
202 } | |
203 | |
204 // There are other streams in this priority level which take precedence. | |
205 // Yield. | |
206 return true; | |
207 } | |
208 | |
209 void MarkStreamReady(StreamIdType stream_id, bool add_to_front) override { | |
210 auto it = stream_infos_.find(stream_id); | |
211 if (it == stream_infos_.end()) { | |
212 SPDY_BUG << "Stream " << stream_id << " not registered"; | |
213 return; | |
214 } | |
215 StreamInfo& stream_info = it->second; | |
216 if (stream_info.ready) { | |
217 return; | |
218 } | |
219 ReadyList& ready_list = priority_infos_[stream_info.priority].ready_list; | |
220 if (add_to_front) { | |
221 ready_list.push_front(&stream_info); | |
222 } else { | |
223 ready_list.push_back(&stream_info); | |
224 } | |
225 ++num_ready_streams_; | |
226 stream_info.ready = true; | |
227 } | |
228 | |
229 void MarkStreamNotReady(StreamIdType stream_id) override { | |
230 auto it = stream_infos_.find(stream_id); | |
231 if (it == stream_infos_.end()) { | |
232 SPDY_BUG << "Stream " << stream_id << " not registered"; | |
233 return; | |
234 } | |
235 StreamInfo& stream_info = it->second; | |
236 if (!stream_info.ready) { | |
237 return; | |
238 } | |
239 bool erased = | |
240 Erase(&priority_infos_[stream_info.priority].ready_list, stream_info); | |
241 DCHECK(erased); | |
242 stream_info.ready = false; | |
243 } | |
244 | |
245 // Returns true iff the number of ready streams is non-zero. | |
246 bool HasReadyStreams() const override { return num_ready_streams_ > 0; } | |
247 | |
248 // Returns the number of ready streams. | |
249 size_t NumReadyStreams() const override { return num_ready_streams_; } | |
250 | |
251 private: | |
252 friend class test::PriorityWriteSchedulerPeer<StreamIdType>; | |
253 | |
254 // State kept for all registered streams. All ready streams have ready = true | |
255 // and should be present in priority_infos_[priority].ready_list. | |
256 struct StreamInfo { | |
257 SpdyPriority priority; | |
258 StreamIdType stream_id; | |
259 bool ready; | |
260 }; | |
261 | |
262 // 0(1) size lookup, 0(1) insert at front or back. | |
263 typedef std::deque<StreamInfo*> ReadyList; | |
264 | |
265 // State kept for each priority level. | |
266 struct PriorityInfo { | |
267 // IDs of streams that are ready to write. | |
268 ReadyList ready_list; | |
269 // Time of latest write event for stream of this priority, in microseconds. | |
270 int64_t last_event_time_usec = 0; | |
271 }; | |
272 | |
273 typedef std::unordered_map<StreamIdType, StreamInfo> StreamInfoMap; | |
274 | |
275 // Erases first occurrence (which should be the only one) of |info| in | |
276 // |ready_list|, returning true if found (and erased), or false otherwise. | |
277 // Decrements |num_ready_streams_| if an entry is erased. | |
278 bool Erase(ReadyList* ready_list, const StreamInfo& info) { | |
279 auto it = std::find(ready_list->begin(), ready_list->end(), &info); | |
280 if (it == ready_list->end()) { | |
281 return false; | |
282 } | |
283 ready_list->erase(it); | |
284 --num_ready_streams_; | |
285 return true; | |
286 } | |
287 | |
288 // Number of ready streams. | |
289 size_t num_ready_streams_ = 0; | |
290 // Per-priority state, including ready lists. | |
291 PriorityInfo priority_infos_[kV3LowestPriority + 1]; | |
292 // StreamInfos for all registered streams. | |
293 StreamInfoMap stream_infos_; | |
294 }; | |
295 | |
296 } // namespace net | |
297 | |
298 #endif // NET_SPDY_PRIORITY_WRITE_SCHEDULER_H_ | |
OLD | NEW |