OLD | NEW |
---|---|
(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 | |
OLD | NEW |