Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: net/base/network_throttle_manager_impl.cc

Issue 2130493002: Implement THROTTLED priority semantics. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@NetworkStreamThrottler
Patch Set: Added (currently failing) URLRequest unit test. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 #include "net/base/network_throttle_manager_impl.h"
6
7 #include <algorithm>
8
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/time/default_tick_clock.h"
12
13 namespace net {
14
15 const size_t NetworkThrottleManagerImpl::kActiveRequestThrottlingLimit = 2;
16 const int NetworkThrottleManagerImpl::kMedianLifetimeMultiple = 5;
17 const int NetworkThrottleManagerImpl::kInitialMedianInMs = 100;
Charlie Harrison 2016/09/01 18:17:52 Brief comment where you get this number would be h
Randy Smith (Not in Mondays) 2016/09/18 19:12:34 You have more faith in me than is valid--this numb
Charlie Harrison 2016/09/19 16:35:35 Can you use Net.RequestTime2.Success? Median looks
Randy Smith (Not in Mondays) 2016/09/22 21:52:26 Done, though since the distribution was bi-modal I
18
19 NetworkThrottleManagerImpl::ThrottleImpl::ThrottleImpl(
20 bool blocked,
21 RequestPriority priority,
22 NetworkThrottleManager::ThrottleDelegate* delegate,
23 NetworkThrottleManagerImpl* manager,
24 QueuePointer queue_pointer)
25 : blocked_(blocked),
26 priority_(priority),
27 delegate_(delegate),
28 manager_(manager),
29 queue_pointer_(queue_pointer) {
30 DCHECK(delegate);
31 if (!blocked_)
32 start_time_ = manager->tick_clock_->NowTicks();
33 }
34
35 NetworkThrottleManagerImpl::ThrottleImpl::~ThrottleImpl() {
36 manager_->OnThrottleDestroyed(this);
37 }
38
39 bool NetworkThrottleManagerImpl::ThrottleImpl::IsBlocked() const {
40 return blocked_;
41 }
42
43 RequestPriority NetworkThrottleManagerImpl::ThrottleImpl::Priority() const {
44 return priority_;
45 }
46
47 void NetworkThrottleManagerImpl::ThrottleImpl::SetPriority(
48 RequestPriority new_priority) {
49 RequestPriority old_priority(priority_);
50 if (old_priority == new_priority)
51 return;
52 priority_ = new_priority;
53 manager_->OnThrottlePriorityChanged(this, old_priority, new_priority);
54 }
55
56 void NetworkThrottleManagerImpl::ThrottleImpl::NotifyUnblocked() {
57 // This methods should only be called once, and only if the
58 // current state is blocked.
59 DCHECK(blocked_);
60 blocked_ = false;
61 delegate_->OnThrottleStateChanged(this);
62 }
63
64 NetworkThrottleManagerImpl::NetworkThrottleManagerImpl()
65 : lifetime_median_estimate_(50, kInitialMedianInMs),
Charlie Harrison 2016/09/01 18:17:52 Magic numbers scare me. As a reader it would be ni
Randy Smith (Not in Mondays) 2016/09/18 19:12:34 Done.
66 start_time_ordering_set_(
67 &NetworkThrottleManagerImpl::CompareThrottlesForCreationTime),
68 num_throttles_blocked_(0u),
69 num_effective_outstanding_(0u),
70 outstanding_recomputation_timer_(false /* retain_user_task */,
71 false /* is_repeating */),
72 tick_clock_(new base::DefaultTickClock()) {
73 outstanding_recomputation_timer_.SetTaskRunner(
74 base::MessageLoop::current()->task_runner());
75 }
76
77 NetworkThrottleManagerImpl::~NetworkThrottleManagerImpl() {}
78
79 std::unique_ptr<NetworkThrottleManager::Throttle>
80 NetworkThrottleManagerImpl::CreateThrottle(
81 NetworkThrottleManager::ThrottleDelegate* delegate,
82 RequestPriority priority,
83 bool ignore_limits) {
84 bool blocked = (!ignore_limits && priority == THROTTLED &&
85 num_effective_outstanding_ >= kActiveRequestThrottlingLimit);
86
87 std::unique_ptr<NetworkThrottleManagerImpl::ThrottleImpl> throttle(
88 new ThrottleImpl(blocked, priority, delegate, this,
89 // Just to have a meaningful default value
90 unblocked_requests_[priority].end()));
91
92 ThrottleImpl::ThrottleList* throttle_list =
93 ListForThrottle(blocked, priority);
94
95 throttle->set_queue_pointer(
96 throttle_list->insert(throttle_list->end(), throttle.get()));
97 if (blocked)
98 ++num_throttles_blocked_;
99 else
100 start_time_ordering_set_.insert(throttle.get());
101
102 // Can only have increased the outstanding count (decreases due to aging out
103 // would be caught by timer) so a MaybeUnblockThrottles() isn't needed.
104 RecomputeOutstanding();
105 DCHECK_EQ(num_throttles_blocked_, NumberBlockedThrottles());
mmenke 2016/09/01 19:14:23 If !blocked(), this doesn't get us anything, does
Randy Smith (Not in Mondays) 2016/09/18 19:12:34 RecomputeOustanding() does two things: It modifies
106
107 return std::move(throttle);
108 }
109
110 void NetworkThrottleManagerImpl::SetTickClockForTesting(
111 std::unique_ptr<base::TickClock> tick_clock) {
112 tick_clock_ = std::move(tick_clock);
113 }
114
115 void NetworkThrottleManagerImpl::ConditionallyTriggerTimerForTesting() {
116 // Relies on |!retain_user_task| in timer constructor.
117 if (!outstanding_recomputation_timer_.user_task().is_null() &&
118 (tick_clock_->NowTicks() >
119 outstanding_recomputation_timer_.desired_run_time())) {
120 base::Closure timer_callback(outstanding_recomputation_timer_.user_task());
121 outstanding_recomputation_timer_.Stop();
122 timer_callback.Run();
123 }
124 }
125
126 bool NetworkThrottleManagerImpl::CompareThrottlesForCreationTime(
mmenke 2016/09/01 19:14:22 ForCreationTime -> ByCreationTime? Also kind wond
Randy Smith (Not in Mondays) 2016/09/18 19:12:34 Your question about "compare" got me chasing what
127 ThrottleImpl* throttle1,
128 ThrottleImpl* throttle2) {
129 // No throttle should be in the CreationOrderingSet with a null start
130 // time.
131 DCHECK_NE(throttle1->start_time(), base::TimeTicks());
132 DCHECK_NE(throttle2->start_time(), base::TimeTicks());
133 return (throttle1->start_time() < throttle2->start_time() ||
134 // So different throttles don't look equal to the comparison
135 // function.
136 (throttle1->start_time() == throttle2->start_time() &&
137 throttle1 < throttle2));
138 }
139
140 void NetworkThrottleManagerImpl::OnThrottlePriorityChanged(
141 NetworkThrottleManagerImpl::ThrottleImpl* throttle,
142 RequestPriority old_priority,
143 RequestPriority new_priority) {
144 ThrottleImpl::ThrottleList* old_throttle_list =
145 ListForThrottle(throttle->IsBlocked(), old_priority);
146 DCHECK(std::find(old_throttle_list->begin(), old_throttle_list->end(),
147 throttle) != old_throttle_list->end());
148
149 old_throttle_list->erase(throttle->queue_pointer());
150
151 // Check to see if this priority change is changing blocking state.
152 // Currently the only possible transition is from THROTTLED/blocked
153 // to UNTHROTTLED/blocked, so just check for that.
154 bool unblocking = (old_priority == THROTTLED && new_priority != THROTTLED &&
155 throttle->IsBlocked());
156 ThrottleImpl::ThrottleList* new_throttle_list =
157 ListForThrottle(unblocking ? false : throttle->IsBlocked(), new_priority);
158
159 throttle->set_queue_pointer(
160 new_throttle_list->insert(new_throttle_list->end(), throttle));
161
162 // There is no need to call |MaybeUnblockThrottles()| because
163 // changing a throttle priority will not have decreased the number of
164 // outstanding throttles.
165
166 if (unblocking) {
mmenke 2016/09/01 19:14:22 Can this just call UnblockThrottle?
Randy Smith (Not in Mondays) 2016/09/18 19:12:34 UnblockThrottles does queue management which is re
167 // NOTE: This call may result in reentrant calls into
168 // NetworkThrottleManagerImpl; no state should be assumed to be
169 // persistent across this call.
170 --num_throttles_blocked_;
171 throttle->set_start_time(tick_clock_->NowTicks());
172 start_time_ordering_set_.insert(throttle);
173 throttle->NotifyUnblocked();
174 }
175 DCHECK_EQ(num_throttles_blocked_, NumberBlockedThrottles());
176 }
177
178 void NetworkThrottleManagerImpl::OnThrottleDestroyed(ThrottleImpl* throttle) {
179 // If the throttle has a start time, register its lifetime.
180 if (!throttle->IsBlocked()) {
181 DCHECK_NE(throttle->start_time(), base::TimeTicks());
182
183 lifetime_median_estimate_.AddSample(
184 (tick_clock_->NowTicks() - throttle->start_time())
185 .InMillisecondsRoundedUp());
186 }
187
188 ThrottleImpl::ThrottleList* throttle_list = ListForThrottle(throttle);
189 DCHECK(std::find(throttle_list->begin(), throttle_list->end(), throttle) !=
190 throttle_list->end());
191 throttle_list->erase(throttle->queue_pointer());
192 if (throttle->IsBlocked()) {
193 --num_throttles_blocked_;
194 } else {
195 size_t items_erased = start_time_ordering_set_.erase(throttle);
196 DCHECK_EQ(1u, items_erased);
197 }
198
199 // TODO(rdsmith): Via PostTask so there aren't upcalls from within
200 // destructors?
mmenke 2016/09/01 19:14:23 +1
Randy Smith (Not in Mondays) 2016/09/18 19:12:34 Done.
201 MaybeUnblockThrottles();
202 DCHECK_EQ(num_throttles_blocked_, NumberBlockedThrottles());
203 }
204
205 void NetworkThrottleManagerImpl::RecomputeOutstanding() {
206 DCHECK_EQ(num_throttles_blocked_, NumberBlockedThrottles());
207 outstanding_recomputation_timer_.Stop();
208
209 num_effective_outstanding_ = start_time_ordering_set_.size();
210 if (num_effective_outstanding_ == 0) {
211 outstanding_recomputation_timer_.Stop();
212 return;
213 }
214
215 // The number of throttles that "count" as outstanding is the
216 // total number of throttles minus all throttles that are
217 // older than |kMedianLifetimeMultiple| times the lifetime median
218 // estimate.
219 int median_estimate_in_ms = lifetime_median_estimate_.current_estimate();
220 if (median_estimate_in_ms == 0)
221 median_estimate_in_ms = 1;
222
223 base::TimeTicks age_horizon(
224 tick_clock_->NowTicks() -
225 (kMedianLifetimeMultiple *
226 base::TimeDelta::FromMilliseconds(median_estimate_in_ms)));
227 for (const auto* throttle : start_time_ordering_set_) {
228 DCHECK_NE(throttle->start_time(), base::TimeTicks());
229 if (throttle->start_time() > age_horizon) {
230 // The earliest we might need to re-evaluate the outstanding count
231 // and blocked status is the amount of time it will take the current
232 // element to age out of the effective set.
233 outstanding_recomputation_timer_.Start(
234 FROM_HERE, throttle->start_time() - age_horizon,
235 // Unretained use of |this| is safe because the timer is
236 // owned by this object, and will be torn down if this object
237 // is destroyed.
238 base::Bind(&NetworkThrottleManagerImpl::MaybeUnblockThrottles,
239 base::Unretained(this)));
240 break;
241 }
242
243 // Throttles before the age_horizon have aged out of being counted
244 // as outstanding.
245 num_effective_outstanding_--;
246 }
247 }
248
249 void NetworkThrottleManagerImpl::UnblockThrottle(ThrottleImpl* throttle) {
250 DCHECK(throttle->IsBlocked());
251
252 ThrottleImpl::ThrottleList* throttle_list = ListForThrottle(throttle);
253 RequestPriority priority = throttle->priority();
254
255 DCHECK(std::find(throttle_list->begin(), throttle_list->end(), throttle) !=
256 throttle_list->end());
257 ListForThrottle(throttle)->erase(throttle->queue_pointer());
258
259 throttle->set_queue_pointer(unblocked_requests_[priority].insert(
260 unblocked_requests_[priority].end(), throttle));
261
262 throttle->set_start_time(tick_clock_->NowTicks());
263 start_time_ordering_set_.insert(throttle);
264
265 // No need to call RecomputeOutstanding() here as we know the just
266 // unblocked throttle counts as outstanding, because the start time
267 // is NowTicks().
268 ++num_effective_outstanding_;
269 --num_throttles_blocked_;
270
271 // May result in re-entrant calls into this class.
272 throttle->NotifyUnblocked();
273 }
274
275 void NetworkThrottleManagerImpl::MaybeUnblockThrottles() {
276 RecomputeOutstanding();
277
278 while (num_effective_outstanding_ < kActiveRequestThrottlingLimit &&
279 !blocked_requests_[THROTTLED].empty()) {
280 // NOTE: This call may result in reentrant calls into
281 // NetworkThrottleManagerImpl; no state should be assumed to be
282 // persistent across this call.
283 UnblockThrottle(blocked_requests_[THROTTLED].front());
284 }
285 DCHECK_EQ(num_throttles_blocked_, NumberBlockedThrottles());
286 }
287
288 std::list<NetworkThrottleManagerImpl::ThrottleImpl*>*
289 NetworkThrottleManagerImpl::ListForThrottle(bool blocked,
290 RequestPriority priority) {
291 return &((blocked ? blocked_requests_ : unblocked_requests_)[priority]);
292 }
293
294 std::list<NetworkThrottleManagerImpl::ThrottleImpl*>*
295 NetworkThrottleManagerImpl::ListForThrottle(ThrottleImpl* throttle) {
296 return ListForThrottle(throttle->IsBlocked(), throttle->priority());
297 }
298
299 size_t NetworkThrottleManagerImpl::NumberBlockedThrottles() const {
300 size_t blocked_count = 0u;
301 for (int i = 0; i < NUM_PRIORITIES; ++i)
302 blocked_count += blocked_requests_[i].size();
303 return blocked_count;
304 }
305
306 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698