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

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 tests and cleaned up code. Created 4 years, 5 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 "base/logging.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/time/default_clock.h"
10
11 namespace net {
12
13 const size_t NetworkThrottleManagerImpl::kActiveRequestThrottlingLimit = 2;
Charlie Harrison 2016/07/07 20:17:26 Please document these constants. They frighten me.
Randy Smith (Not in Mondays) 2016/07/08 20:54:23 What could possibly be frightening about a couple
14 const double NetworkThrottleManagerImpl::kMedianEstimatorLearningParameter = 1;
15 const int NetworkThrottleManagerImpl::kInitialMedianInMS = 100;
16 const int NetworkThrottleManagerImpl::kMedianLifetimeMultiple = 5;
17
18 NetworkThrottleManagerImpl::ThrottleImpl::ThrottleImpl(
19 bool blocked,
20 RequestPriority priority,
21 NetworkThrottleManager::ThrottleDelegate* delegate,
22 NetworkThrottleManagerImpl* manager,
23 QueuePointer queue_pointer)
24 : blocked_(blocked),
25 priority_(priority),
26 delegate_(delegate),
27 manager_(manager),
28 creation_time_(manager->clock_->Now()),
29 queue_pointer_(queue_pointer) {
30 DCHECK(delegate);
31 }
32
33 NetworkThrottleManagerImpl::ThrottleImpl::~ThrottleImpl() {
34 manager_->OnThrottleDestroyed(this);
35 }
36
37 bool NetworkThrottleManagerImpl::ThrottleImpl::IsBlocked() const {
38 return blocked_;
39 }
40
41 RequestPriority NetworkThrottleManagerImpl::ThrottleImpl::Priority() const {
42 return priority_;
43 }
44
45 void NetworkThrottleManagerImpl::ThrottleImpl::SetPriority(
46 RequestPriority new_priority) {
47 RequestPriority old_priority(priority_);
48 priority_ = new_priority;
49 manager_->OnThrottlePriorityChanged(this, old_priority, new_priority);
50 }
51
52 void NetworkThrottleManagerImpl::ThrottleImpl::NotifyUnblocked() {
53 // This methods should only be called once, and only if the
54 // current state is blocked.
55 DCHECK(blocked_);
56 blocked_ = false;
57 delegate_->OnThrottleStateChanged(this);
58 }
59
60 NetworkThrottleManagerImpl::NetworkThrottleManagerImpl()
61 : lifetime_median_estimate_(
62 base::TimeDelta::FromMilliseconds(kInitialMedianInMS)),
63 creation_ordering_set_(
64 &NetworkThrottleManagerImpl::CompareThrottlesForCreationTime),
65 num_throttles_blocked_(0u),
66 num_effective_outstanding_(0u),
67 outstanding_recomputation_timer_(true, false),
Charlie Harrison 2016/07/07 20:17:26 Do you mind saying: outstanding_recomputation_time
Randy Smith (Not in Mondays) 2016/07/08 20:54:23 Done.
68 clock_(new base::DefaultClock()) {
Charlie Harrison 2016/07/07 20:17:25 This clock uses wall time. Shouldn't you use monot
Randy Smith (Not in Mondays) 2016/07/08 20:54:24 Good catch. Done.
69 outstanding_recomputation_timer_.SetTaskRunner(
70 base::MessageLoop::current()->task_runner());
71 }
72
73 NetworkThrottleManagerImpl::~NetworkThrottleManagerImpl() {}
74
75 std::unique_ptr<NetworkThrottleManager::Throttle>
76 NetworkThrottleManagerImpl::CreateThrottle(
77 NetworkThrottleManager::ThrottleDelegate* delegate,
78 RequestPriority priority,
79 bool ignore_limits) {
80 bool blocked = (!ignore_limits && priority == THROTTLED &&
81 num_effective_outstanding_ >= kActiveRequestThrottlingLimit);
82
83 std::unique_ptr<NetworkThrottleManagerImpl::ThrottleImpl> throttle(
84 new ThrottleImpl(blocked, priority, delegate, this,
85 // Just to have a meaningful default value
86 unblocked_requests_[priority].end()));
87
88 std::list<ThrottleImpl*>* throttle_list = ListForThrottle(blocked, priority);
89
90 throttle->set_queue_pointer(
91 throttle_list->insert(throttle_list->end(), throttle.get()));
92 creation_ordering_set_.insert(throttle.get());
93 if (blocked)
94 ++num_throttles_blocked_;
95
96 RecomputeOutstanding();
97 // Can only have increased the outstanding count (decreases due to
Charlie Harrison 2016/07/07 20:17:26 nit: You can fit more words on this comment line.
Randy Smith (Not in Mondays) 2016/07/08 20:54:23 Done but if git format changes it back I'm not goi
Charlie Harrison 2016/07/08 22:36:05 The problem with git cl format is that it happily
98 // aging out would be caught by timer) so a MaybeUnthrottle() isn't needed.
Charlie Harrison 2016/07/07 20:17:26 s/MaybeUnthrottle/MaybeUnblock/
Randy Smith (Not in Mondays) 2016/07/08 20:54:23 Ooops. Query replace failure. Done.
99
100 return std::move(throttle);
101 }
102
103 void NetworkThrottleManagerImpl::SetClockForTesting(
104 std::unique_ptr<base::Clock> clock) {
105 clock_ = std::move(clock);
106 }
107
108 bool NetworkThrottleManagerImpl::CompareThrottlesForCreationTime(
109 ThrottleImpl* throttle1,
110 ThrottleImpl* throttle2) {
111 return (throttle1->creation_time() < throttle2->creation_time() ||
112 // So different throttles don't look equal to the comparison
113 // function.
114 (throttle1->creation_time() == throttle2->creation_time() &&
115 throttle1 < throttle2));
116 }
117
118 void NetworkThrottleManagerImpl::OnThrottlePriorityChanged(
119 NetworkThrottleManagerImpl::ThrottleImpl* throttle,
120 RequestPriority old_priority,
121 RequestPriority new_priority) {
122 ListForThrottle(throttle->IsBlocked(), old_priority)
Charlie Harrison 2016/07/07 20:17:26 Can you either: 1. DCHECK in the throttle impl tha
Randy Smith (Not in Mondays) 2016/07/08 20:54:24 So possibly the fact that you and Matt have called
Charlie Harrison 2016/07/08 22:36:05 You raise a good point, but I think my mind has no
Randy Smith (Not in Mondays) 2016/08/29 19:44:51 Ok, I yield. Done (no-op if priority is the same,
123 ->erase(throttle->queue_pointer());
124
125 std::list<ThrottleImpl*>* new_throttle_list =
126 ListForThrottle(throttle->IsBlocked(), new_priority);
127
128 throttle->set_queue_pointer(
129 new_throttle_list->insert(new_throttle_list->end(), throttle));
130
131 // |MaybeUnblock()|, below, only checks the THROTTLED priority; if |throttle|
Charlie Harrison 2016/07/07 20:17:26 nit: Remove the commas and replace the semicolon w
Randy Smith (Not in Mondays) 2016/07/08 20:54:23 Done.
132 // was transitioned away from that priority it may need to be unblocked.
133 if (old_priority == THROTTLED && new_priority != THROTTLED &&
134 throttle->IsBlocked()) {
135 // NOTE: This call may result in reentrant calls into
136 // NetworkThrottleManagerImpl; no state should be assumed to be
137 // persistent across this call.
138 --num_throttles_blocked_;
139 throttle->NotifyUnblocked();
140 }
141
142 MaybeUnblock();
Charlie Harrison 2016/07/07 20:17:26 I don't really like this method name but I can't c
Randy Smith (Not in Mondays) 2016/07/08 20:54:23 Horrible enough to be funny, at least :-}. I thin
143 }
144
145 void NetworkThrottleManagerImpl::OnThrottleDestroyed(ThrottleImpl* throttle) {
146 // Adjust median estimate. Algorithm from
147 // http://stackoverflow.com/questions/1309263/rolling-median-algorithm-in-c
Charlie Harrison 2016/07/07 20:17:26 Better link: this paper has the algorithm and expl
Randy Smith (Not in Mondays) 2016/07/08 20:54:23 Wow. Nice reference. I think estimating the roll
148 lifetime_median_estimate_ += base::TimeDelta::FromMilliseconds(
149 kMedianEstimatorLearningParameter *
150 (clock_->Now() - throttle->creation_time() > lifetime_median_estimate_
151 ? 1
152 : -1));
153
154 if (lifetime_median_estimate_ < base::TimeDelta::FromMilliseconds(1))
155 lifetime_median_estimate_ = base::TimeDelta::FromMilliseconds(1);
156
157 ListForThrottle(throttle)->erase(throttle->queue_pointer());
158 creation_ordering_set_.erase(throttle);
Charlie Harrison 2016/07/07 20:17:26 Can you DCHECK(creation_ordering_set_.erase(thrott
Randy Smith (Not in Mondays) 2016/07/08 20:54:23 Yes, conceptually, but I don't want the erase insi
159 if (throttle->IsBlocked())
160 --num_throttles_blocked_;
161 RecomputeOutstanding();
162
163 // TODO(rdsmith): Via PostTask so there aren't upcalls from within
164 // destructors?
165 MaybeUnblock();
166 }
167
168 void NetworkThrottleManagerImpl::RecomputeOutstanding() {
169 num_effective_outstanding_ =
170 creation_ordering_set_.size() - num_throttles_blocked_;
171 if (num_effective_outstanding_ == 0) {
172 outstanding_recomputation_timer_.Stop();
173 return;
174 }
175
176 // The number of throttles that "count" as outstanding is the
177 // total number of throttles minus all throttles that are
178 // older than |kMedianLifetimeMultiple * lifetime_median_estimate_|.
179 base::Time age_horizon(clock_->Now() -
180 kMedianLifetimeMultiple * lifetime_median_estimate_);
181 for (auto it = creation_ordering_set_.begin();
Charlie Harrison 2016/07/07 20:17:26 Can you just use a range for loop here?
Randy Smith (Not in Mondays) 2016/07/08 20:54:24 Fair point; done.
182 it != creation_ordering_set_.end(); ++it) {
183 if ((*it)->creation_time() > age_horizon) {
184 // The earliest we might need to recompute the outstanding count
185 // is the amount of time it will take the current element to age
186 // out of the effective set.
187 outstanding_recomputation_timer_.Start(
188 FROM_HERE, (*it)->creation_time() - age_horizon,
189 // Unretained use of |this| is safe because the timer is
190 // owned by this object, and will be torn down if this object
191 // is destroyed.
192 base::Bind(&NetworkThrottleManagerImpl::RecomputeOutstanding,
Charlie Harrison 2016/07/07 20:17:25 Don't we need to recompute outstanding AND maybe u
Randy Smith (Not in Mondays) 2016/07/08 20:54:24 Good catch. This requires a test case, which I'm
193 base::Unretained(this)));
194 break;
195 }
196
197 // Only unblocked throttles are considered outstanding, so only those
198 // throttles need to be subtracted out due to aging.
199 if (!(*it)->IsBlocked())
200 --num_effective_outstanding_;
201 }
202 }
203
204 void NetworkThrottleManagerImpl::MaybeUnblock() {
205 while (num_effective_outstanding_ < kActiveRequestThrottlingLimit &&
206 !blocked_requests_[THROTTLED].empty()) {
207 ThrottleImpl* to_unblock = blocked_requests_[THROTTLED].front();
208 blocked_requests_[THROTTLED].pop_front();
209
210 to_unblock->set_queue_pointer(unblocked_requests_[THROTTLED].insert(
211 unblocked_requests_[THROTTLED].end(), to_unblock));
212
213 ++num_effective_outstanding_;
214 --num_throttles_blocked_;
215
216 // NOTE: This call may result in reentrant calls into
217 // NetworkThrottleManagerImpl; no state should be assumed to be
218 // persistent across this call.
219 to_unblock->NotifyUnblocked();
220 }
221 }
222
223 std::list<NetworkThrottleManagerImpl::ThrottleImpl*>*
224 NetworkThrottleManagerImpl::ListForThrottle(bool blocked,
225 RequestPriority priority) {
226 return &((blocked ? blocked_requests_ : unblocked_requests_)[priority]);
227 }
228
229 std::list<NetworkThrottleManagerImpl::ThrottleImpl*>*
230 NetworkThrottleManagerImpl::ListForThrottle(ThrottleImpl* throttle) {
231 return ListForThrottle(throttle->IsBlocked(), throttle->priority());
232 }
233
234 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698