Chromium Code Reviews| 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 <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 | |
| OLD | NEW |