1 // Copyright (c) 2012 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 "services/network/resource_scheduler/resource_scheduler.h"
6
7 #include <stdint.h>
8
9 #include <string>
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/macros.h"
14 #include "base/metrics/field_trial.h"
15 #include "base/metrics/field_trial_params.h"
16 #include "base/metrics/histogram_functions.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/optional.h"
19 #include "base/sequenced_task_runner.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/supports_user_data.h"
23 #include "base/threading/thread_task_runner_handle.h"
24 #include "base/time/default_tick_clock.h"
25 #include "base/time/tick_clock.h"
26 #include "base/trace_event/trace_event.h"
27 #include "net/base/host_port_pair.h"
28 #include "net/base/isolation_info.h"
29 #include "net/base/load_flags.h"
30 #include "net/base/request_priority.h"
31 #include "net/http/http_server_properties.h"
32 #include "net/log/net_log.h"
33 #include "net/nqe/effective_connection_type_observer.h"
34 #include "net/nqe/network_quality_estimator.h"
35 #include "net/nqe/peer_to_peer_connections_count_observer.h"
36 #include "net/url_request/url_request.h"
37 #include "net/url_request/url_request_context.h"
38 #include "services/network/public/cpp/features.h"
39 #include "services/network/public/mojom/network_context.mojom.h"
40 #include "url/scheme_host_port.h"
41
42 namespace network {
43
44 namespace {
45
46 enum StartMode { START_SYNC, START_ASYNC };
47
48 // Flags identifying various attributes of the request that are used
49 // when making scheduling decisions.
50 using RequestAttributes = uint8_t;
51 const RequestAttributes kAttributeNone = 0x00;
52 const RequestAttributes kAttributeInFlight = 0x01;
53 const RequestAttributes kAttributeDelayable = 0x02;
54 const RequestAttributes kAttributeLayoutBlocking = 0x04;
55
56 // Reasons why pending requests may be started. For logging only.
57 enum class RequestStartTrigger {
58 NONE,
59 COMPLETION_PRE_BODY,
60 COMPLETION_POST_BODY,
61 BODY_REACHED,
62 CLIENT_KILL,
63 SPDY_PROXY_DETECTED,
64 REQUEST_REPRIORITIZED,
65 LONG_QUEUED_REQUESTS_TIMER_FIRED,
66 EFFECTIVE_CONNECTION_TYPE_CHANGED,
67 PEER_TO_PEER_CONNECTIONS_COUNT_CHANGED,
68 };
69
RequestStartTriggerString(RequestStartTrigger trigger)70 const char* RequestStartTriggerString(RequestStartTrigger trigger) {
71 switch (trigger) {
72 case RequestStartTrigger::NONE:
73 return "NONE";
74 case RequestStartTrigger::COMPLETION_PRE_BODY:
75 return "COMPLETION_PRE_BODY";
76 case RequestStartTrigger::COMPLETION_POST_BODY:
77 return "COMPLETION_POST_BODY";
78 case RequestStartTrigger::BODY_REACHED:
79 return "BODY_REACHED";
80 case RequestStartTrigger::CLIENT_KILL:
81 return "CLIENT_KILL";
82 case RequestStartTrigger::SPDY_PROXY_DETECTED:
83 return "SPDY_PROXY_DETECTED";
84 case RequestStartTrigger::REQUEST_REPRIORITIZED:
85 return "REQUEST_REPRIORITIZED";
86 case RequestStartTrigger::LONG_QUEUED_REQUESTS_TIMER_FIRED:
87 return "LONG_QUEUED_REQUESTS_TIMER_FIRED";
88 case RequestStartTrigger::EFFECTIVE_CONNECTION_TYPE_CHANGED:
89 return "EFFECTIVE_CONNECTION_TYPE_CHANGED";
90 case RequestStartTrigger::PEER_TO_PEER_CONNECTIONS_COUNT_CHANGED:
91 return "PEER_TO_PEER_CONNECTIONS_COUNT_CHANGED";
92 }
93 }
94
95 } // namespace
96
97 // The maximum number of requests to allow be in-flight at any point in time per
98 // host. This limit does not apply to hosts that support request prioritization
99 // when |delay_requests_on_multiplexed_connections| is true.
100 static const size_t kMaxNumDelayableRequestsPerHostPerClient = 6;
101
102 // The maximum number of delayable requests to allow to be in-flight at any
103 // point in time while in the layout-blocking phase of loading.
104 static const size_t kMaxNumDelayableWhileLayoutBlockingPerClient = 1;
105
106 // The priority level below which resources are considered to be delayable.
107 static const net::RequestPriority kDelayablePriorityThreshold = net::MEDIUM;
108
109 // The number of in-flight layout-blocking requests above which all delayable
110 // requests should be blocked.
111 static const size_t kInFlightNonDelayableRequestCountPerClientThreshold = 1;
112
113 // Returns the duration after which the timer to dispatch queued requests should
114 // fire.
GetQueuedRequestsDispatchPeriodicity()115 base::TimeDelta GetQueuedRequestsDispatchPeriodicity() {
116 // This primarily affects two types of requests:
117 // (i) Requests that have been queued for too long, and firing of timer may
118 // result in some of those requests being dispatched to the network.
119 // Such requests need to be queued for at least 15 seconds before they can be
120 // dispatched.
121 // (ii) Requests that were throttled proactively in anticipation of arrival
122 // of higher priority requests. These requests need to be unthrottled after
123 // their queuing duration expires. The queuing duration is a function of HTTP
124 // RTT and can be of the order of 100 milliseconds.
125 //
126 // Note that the timer is active and fires periodically only if
127 // there is at least one queued request.
128
129 // When kProactivelyThrottleLowPriorityRequests is not enabled, it's
130 // sufficient to choose a longer periodicity (implying that timer will be
131 // fired less frequently) since the requests are queued for at least 15
132 // seconds anyways. Firing the timer a bit later is not going to delay the
133 // dispatch of the request by a significant amount.
134 if (!base::FeatureList::IsEnabled(
135 features::kProactivelyThrottleLowPriorityRequests)) {
136 return base::TimeDelta::FromSeconds(5);
137 }
138
139 // Choosing 100 milliseconds as the checking interval ensurs that the
140 // queue is not checked too frequently. The interval is also not too long, so
141 // we do not expect too many requests to go on the network at the
142 // same time.
143 return base::TimeDelta::FromMilliseconds(
144 base::GetFieldTrialParamByFeatureAsInt(
145 features::kProactivelyThrottleLowPriorityRequests,
146 "queued_requests_dispatch_periodicity_ms", 100));
147 }
148
149 struct ResourceScheduler::RequestPriorityParams {
RequestPriorityParamsnetwork::ResourceScheduler::RequestPriorityParams150 RequestPriorityParams()
151 : priority(net::DEFAULT_PRIORITY), intra_priority(0) {}
152
RequestPriorityParamsnetwork::ResourceScheduler::RequestPriorityParams153 RequestPriorityParams(net::RequestPriority priority, int intra_priority)
154 : priority(priority), intra_priority(intra_priority) {}
155
operator ==network::ResourceScheduler::RequestPriorityParams156 bool operator==(const RequestPriorityParams& other) const {
157 return (priority == other.priority) &&
158 (intra_priority == other.intra_priority);
159 }
160
operator !=network::ResourceScheduler::RequestPriorityParams161 bool operator!=(const RequestPriorityParams& other) const {
162 return !(*this == other);
163 }
164
GreaterThannetwork::ResourceScheduler::RequestPriorityParams165 bool GreaterThan(const RequestPriorityParams& other) const {
166 if (priority != other.priority)
167 return priority > other.priority;
168 return intra_priority > other.intra_priority;
169 }
170
171 net::RequestPriority priority;
172 int intra_priority;
173 };
174
175 class ResourceScheduler::RequestQueue {
176 public:
177 using NetQueue =
178 std::multiset<ScheduledResourceRequestImpl*, ScheduledResourceSorter>;
179
RequestQueue()180 RequestQueue() : fifo_ordering_ids_(0) {}
~RequestQueue()181 ~RequestQueue() {}
182
183 // Adds |request| to the queue with given |priority|.
184 void Insert(ScheduledResourceRequestImpl* request);
185
186 // Removes |request| from the queue.
Erase(ScheduledResourceRequestImpl * request)187 void Erase(ScheduledResourceRequestImpl* request) {
188 PointerMap::iterator it = pointers_.find(request);
189 CHECK(it != pointers_.end());
190 queue_.erase(it->second);
191 pointers_.erase(it);
192 }
193
GetNextHighestIterator()194 NetQueue::iterator GetNextHighestIterator() { return queue_.begin(); }
195
End()196 NetQueue::iterator End() { return queue_.end(); }
197
198 // Returns true if |request| is queued.
IsQueued(ScheduledResourceRequestImpl * request) const199 bool IsQueued(ScheduledResourceRequestImpl* request) const {
200 return base::Contains(pointers_, request);
201 }
202
203 // Returns true if no requests are queued.
IsEmpty() const204 bool IsEmpty() const { return queue_.empty(); }
205
206 private:
207 using PointerMap =
208 std::map<ScheduledResourceRequestImpl*, NetQueue::iterator>;
209
MakeFifoOrderingId()210 uint32_t MakeFifoOrderingId() {
211 fifo_ordering_ids_ += 1;
212 return fifo_ordering_ids_;
213 }
214
215 // Used to create an ordering ID for scheduled resources so that resources
216 // with same priority/intra_priority stay in fifo order.
217 uint32_t fifo_ordering_ids_;
218
219 NetQueue queue_;
220 PointerMap pointers_;
221 };
222
ScheduledResourceRequest()223 ResourceScheduler::ScheduledResourceRequest::ScheduledResourceRequest() {}
~ScheduledResourceRequest()224 ResourceScheduler::ScheduledResourceRequest::~ScheduledResourceRequest() {}
225
RunResumeCallback()226 void ResourceScheduler::ScheduledResourceRequest::RunResumeCallback() {
227 std::move(resume_callback_).Run();
228 }
229
230 // This is the handle we return to the URLLoader so it can interact with the
231 // request.
232 class ResourceScheduler::ScheduledResourceRequestImpl
233 : public ScheduledResourceRequest {
234 public:
ScheduledResourceRequestImpl(const ClientId & client_id,net::URLRequest * request,ResourceScheduler * scheduler,const RequestPriorityParams & priority,bool is_async)235 ScheduledResourceRequestImpl(const ClientId& client_id,
236 net::URLRequest* request,
237 ResourceScheduler* scheduler,
238 const RequestPriorityParams& priority,
239 bool is_async)
240 : client_id_(client_id),
241 request_(request),
242 ready_(false),
243 deferred_(false),
244 is_async_(is_async),
245 attributes_(kAttributeNone),
246 scheduler_(scheduler),
247 priority_(priority),
248 fifo_ordering_(0),
249 peak_delayable_requests_in_flight_(0u),
250 host_port_pair_(net::HostPortPair::FromURL(request->url())) {
251 DCHECK(!request_->GetUserData(kUserDataKey));
252 request_->SetUserData(kUserDataKey, std::make_unique<UnownedPointer>(this));
253 }
254
~ScheduledResourceRequestImpl()255 ~ScheduledResourceRequestImpl() override {
256 if ((attributes_ & kAttributeLayoutBlocking) == kAttributeLayoutBlocking) {
257 UMA_HISTOGRAM_COUNTS_100(
258 "ResourceScheduler.PeakDelayableRequestsInFlight.LayoutBlocking",
259 peak_delayable_requests_in_flight_);
260 }
261 if (!((attributes_ & kAttributeDelayable) == kAttributeDelayable)) {
262 UMA_HISTOGRAM_COUNTS_100(
263 "ResourceScheduler.PeakDelayableRequestsInFlight.NonDelayable",
264 peak_delayable_requests_in_flight_);
265 }
266 request_->RemoveUserData(kUserDataKey);
267 scheduler_->RemoveRequest(this);
268 }
269
ForRequest(net::URLRequest * request)270 static ScheduledResourceRequestImpl* ForRequest(net::URLRequest* request) {
271 UnownedPointer* pointer =
272 static_cast<UnownedPointer*>(request->GetUserData(kUserDataKey));
273 return pointer ? pointer->get() : nullptr;
274 }
275
276 // Starts the request. If |start_mode| is START_ASYNC, the request will not
277 // be started immediately.
Start(StartMode start_mode)278 void Start(StartMode start_mode) {
279 DCHECK(!ready_);
280
281 // If the request was deferred, need to start it. Otherwise, will just not
282 // defer starting it in the first place, and the value of |start_mode|
283 // makes no difference.
284 if (deferred_) {
285 // If can't start the request synchronously, post a task to start the
286 // request.
287 if (start_mode == START_ASYNC) {
288 scheduler_->task_runner()->PostTask(
289 FROM_HERE,
290 base::BindOnce(&ScheduledResourceRequestImpl::Start,
291 weak_ptr_factory_.GetWeakPtr(), START_SYNC));
292 return;
293 }
294 deferred_ = false;
295 RunResumeCallback();
296 }
297
298 ready_ = true;
299 }
300
UpdateDelayableRequestsInFlight(size_t delayable_requests_in_flight)301 void UpdateDelayableRequestsInFlight(size_t delayable_requests_in_flight) {
302 peak_delayable_requests_in_flight_ = std::max(
303 peak_delayable_requests_in_flight_, delayable_requests_in_flight);
304 }
305
set_request_priority_params(const RequestPriorityParams & priority)306 void set_request_priority_params(const RequestPriorityParams& priority) {
307 priority_ = priority;
308 }
get_request_priority_params() const309 const RequestPriorityParams& get_request_priority_params() const {
310 return priority_;
311 }
client_id() const312 const ClientId& client_id() const { return client_id_; }
url_request()313 net::URLRequest* url_request() { return request_; }
url_request() const314 const net::URLRequest* url_request() const { return request_; }
is_async() const315 bool is_async() const { return is_async_; }
fifo_ordering() const316 uint32_t fifo_ordering() const {
317 // Ensure that |fifo_ordering_| has been set before it's used.
318 DCHECK_LT(0u, fifo_ordering_);
319 return fifo_ordering_;
320 }
set_fifo_ordering(uint32_t fifo_ordering)321 void set_fifo_ordering(uint32_t fifo_ordering) {
322 fifo_ordering_ = fifo_ordering;
323 }
attributes() const324 RequestAttributes attributes() const { return attributes_; }
set_attributes(RequestAttributes attributes)325 void set_attributes(RequestAttributes attributes) {
326 attributes_ = attributes;
327 }
host_port_pair() const328 const net::HostPortPair& host_port_pair() const { return host_port_pair_; }
329
330 private:
331 class UnownedPointer : public base::SupportsUserData::Data {
332 public:
UnownedPointer(ScheduledResourceRequestImpl * pointer)333 explicit UnownedPointer(ScheduledResourceRequestImpl* pointer)
334 : pointer_(pointer) {}
335
get() const336 ScheduledResourceRequestImpl* get() const { return pointer_; }
337
338 private:
339 ScheduledResourceRequestImpl* const pointer_;
340
341 DISALLOW_COPY_AND_ASSIGN(UnownedPointer);
342 };
343
344 static const void* const kUserDataKey;
345
346 // ScheduledResourceRequest implemnetation
WillStartRequest(bool * defer)347 void WillStartRequest(bool* defer) override { deferred_ = *defer = !ready_; }
348
349 const ClientId client_id_;
350 net::URLRequest* request_;
351 bool ready_;
352 bool deferred_;
353 bool is_async_;
354 RequestAttributes attributes_;
355 ResourceScheduler* scheduler_;
356 RequestPriorityParams priority_;
357 uint32_t fifo_ordering_;
358
359 // Maximum number of delayable requests in-flight when |this| was in-flight.
360 size_t peak_delayable_requests_in_flight_;
361 // Cached to excessive recomputation in ReachedMaxRequestsPerHostPerClient().
362 const net::HostPortPair host_port_pair_;
363
364 base::WeakPtrFactory<ResourceScheduler::ScheduledResourceRequestImpl>
365 weak_ptr_factory_{this};
366
367 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequestImpl);
368 };
369
370 const void* const
371 ResourceScheduler::ScheduledResourceRequestImpl::kUserDataKey =
372 &ResourceScheduler::ScheduledResourceRequestImpl::kUserDataKey;
373
operator ()(const ScheduledResourceRequestImpl * a,const ScheduledResourceRequestImpl * b) const374 bool ResourceScheduler::ScheduledResourceSorter::operator()(
375 const ScheduledResourceRequestImpl* a,
376 const ScheduledResourceRequestImpl* b) const {
377 // Want the set to be ordered first by decreasing priority, then by
378 // decreasing intra_priority.
379 // ie. with (priority, intra_priority)
380 // [(1, 0), (1, 0), (0, 100), (0, 0)]
381 if (a->get_request_priority_params() != b->get_request_priority_params())
382 return a->get_request_priority_params().GreaterThan(
383 b->get_request_priority_params());
384
385 // If priority/intra_priority is the same, fall back to fifo ordering.
386 // std::multiset doesn't guarantee this until c++11.
387 return a->fifo_ordering() < b->fifo_ordering();
388 }
389
Insert(ScheduledResourceRequestImpl * request)390 void ResourceScheduler::RequestQueue::Insert(
391 ScheduledResourceRequestImpl* request) {
392 DCHECK(!base::Contains(pointers_, request));
393 request->set_fifo_ordering(MakeFifoOrderingId());
394 pointers_[request] = queue_.insert(request);
395 }
396
397 // Each client represents a tab.
398 class ResourceScheduler::Client
399 : public net::EffectiveConnectionTypeObserver,
400 public net::PeerToPeerConnectionsCountObserver {
401 public:
Client(bool is_browser_client,net::NetworkQualityEstimator * network_quality_estimator,ResourceScheduler * resource_scheduler,const base::TickClock * tick_clock)402 Client(bool is_browser_client,
403 net::NetworkQualityEstimator* network_quality_estimator,
404 ResourceScheduler* resource_scheduler,
405 const base::TickClock* tick_clock)
406 : is_browser_client_(is_browser_client),
407 in_flight_delayable_count_(0),
408 total_layout_blocking_count_(0),
409 num_skipped_scans_due_to_scheduled_start_(0),
410 network_quality_estimator_(network_quality_estimator),
411 resource_scheduler_(resource_scheduler),
412 tick_clock_(tick_clock) {
413 DCHECK(tick_clock_);
414
415 if (network_quality_estimator_) {
416 effective_connection_type_ =
417 network_quality_estimator_->GetEffectiveConnectionType();
418 UpdateParamsForNetworkQuality();
419 network_quality_estimator_->AddEffectiveConnectionTypeObserver(this);
420 network_quality_estimator_->AddPeerToPeerConnectionsCountObserver(this);
421 }
422 }
423
~Client()424 ~Client() override {
425 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
426 if (network_quality_estimator_) {
427 network_quality_estimator_->RemoveEffectiveConnectionTypeObserver(this);
428 network_quality_estimator_->RemovePeerToPeerConnectionsCountObserver(
429 this);
430 }
431 }
432
ScheduleRequest(const net::URLRequest & url_request,ScheduledResourceRequestImpl * request)433 void ScheduleRequest(const net::URLRequest& url_request,
434 ScheduledResourceRequestImpl* request) {
435 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
436 SetRequestAttributes(request, DetermineRequestAttributes(request));
437 ShouldStartReqResult should_start = ShouldStartRequest(request);
438 if (should_start == START_REQUEST) {
439 // New requests can be started synchronously without issue.
440 StartRequest(request, START_SYNC, RequestStartTrigger::NONE);
441 } else {
442 pending_requests_.Insert(request);
443 }
444 }
445
RemoveRequest(ScheduledResourceRequestImpl * request)446 void RemoveRequest(ScheduledResourceRequestImpl* request) {
447 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
448
449 if (pending_requests_.IsQueued(request)) {
450 pending_requests_.Erase(request);
451 DCHECK(!base::Contains(in_flight_requests_, request));
452 } else {
453 // Record metrics.
454 if (!RequestAttributesAreSet(request->attributes(), kAttributeDelayable))
455 last_non_delayable_request_end_ = tick_clock_->NowTicks();
456 RecordNetworkContentionMetrics(*request);
457 EraseInFlightRequest(request);
458
459 // Removing this request may have freed up another to load.
460 LoadAnyStartablePendingRequests(
461 RequestStartTrigger::COMPLETION_POST_BODY);
462 }
463 }
464
StartAndRemoveAllRequests()465 RequestSet StartAndRemoveAllRequests() {
466 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
467
468 // First start any pending requests so that they will be moved into
469 // in_flight_requests_. This may exceed the limits
470 // kDefaultMaxNumDelayableRequestsPerClient and
471 // kMaxNumDelayableRequestsPerHostPerClient, so this method must not do
472 // anything that depends on those limits before calling
473 // ClearInFlightRequests() below.
474 while (!pending_requests_.IsEmpty()) {
475 ScheduledResourceRequestImpl* request =
476 *pending_requests_.GetNextHighestIterator();
477 pending_requests_.Erase(request);
478 // Starting requests asynchronously ensures no side effects, and avoids
479 // starting a bunch of requests that may be about to be deleted.
480 StartRequest(request, START_ASYNC, RequestStartTrigger::CLIENT_KILL);
481 }
482 RequestSet unowned_requests;
483 for (RequestSet::iterator it = in_flight_requests_.begin();
484 it != in_flight_requests_.end(); ++it) {
485 unowned_requests.insert(*it);
486 (*it)->set_attributes(kAttributeNone);
487 }
488 ClearInFlightRequests();
489 return unowned_requests;
490 }
491
ReprioritizeRequest(ScheduledResourceRequestImpl * request,RequestPriorityParams old_priority_params,RequestPriorityParams new_priority_params)492 void ReprioritizeRequest(ScheduledResourceRequestImpl* request,
493 RequestPriorityParams old_priority_params,
494 RequestPriorityParams new_priority_params) {
495 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
496
497 request->url_request()->SetPriority(new_priority_params.priority);
498 request->set_request_priority_params(new_priority_params);
499 SetRequestAttributes(request, DetermineRequestAttributes(request));
500 if (!pending_requests_.IsQueued(request)) {
501 DCHECK(base::Contains(in_flight_requests_, request));
502 // Request has already started.
503 return;
504 }
505
506 pending_requests_.Erase(request);
507 pending_requests_.Insert(request);
508
509 if (new_priority_params.priority > old_priority_params.priority) {
510 // Check if this request is now able to load at its new priority.
511 ScheduleLoadAnyStartablePendingRequests(
512 RequestStartTrigger::REQUEST_REPRIORITIZED);
513 }
514 }
515
516 // Updates the params based on the current network quality estimate.
UpdateParamsForNetworkQuality()517 void UpdateParamsForNetworkQuality() {
518 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
519
520 params_for_network_quality_ =
521 resource_scheduler_->resource_scheduler_params_manager_
522 .GetParamsForEffectiveConnectionType(
523 network_quality_estimator_
524 ? effective_connection_type_
525 : net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
526 }
527
OnLongQueuedRequestsDispatchTimerFired()528 void OnLongQueuedRequestsDispatchTimerFired() {
529 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
530 LoadAnyStartablePendingRequests(
531 RequestStartTrigger::LONG_QUEUED_REQUESTS_TIMER_FIRED);
532 }
533
HasNoPendingRequests() const534 bool HasNoPendingRequests() const {
535 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
536 return pending_requests_.IsEmpty();
537 }
538
IsActiveResourceSchedulerClient() const539 bool IsActiveResourceSchedulerClient() const {
540 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
541 return (!pending_requests_.IsEmpty() || !in_flight_requests_.empty());
542 }
543
CountInflightDelayableRequests() const544 size_t CountInflightDelayableRequests() const {
545 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
546 return in_flight_delayable_count_;
547 }
548
CountInflightNonDelayableRequests() const549 size_t CountInflightNonDelayableRequests() const {
550 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
551 return in_flight_requests_.size() - in_flight_delayable_count_;
552 }
553
CountInflightLayoutBlockingRequests() const554 size_t CountInflightLayoutBlockingRequests() const {
555 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
556 return total_layout_blocking_count_;
557 }
558
559 private:
560 enum ShouldStartReqResult {
561 DO_NOT_START_REQUEST_AND_STOP_SEARCHING,
562 DO_NOT_START_REQUEST_AND_KEEP_SEARCHING,
563 START_REQUEST
564 };
565
566 // net::EffectiveConnectionTypeObserver:
OnEffectiveConnectionTypeChanged(net::EffectiveConnectionType effective_connection_type)567 void OnEffectiveConnectionTypeChanged(
568 net::EffectiveConnectionType effective_connection_type) override {
569 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
570 if (effective_connection_type_ == effective_connection_type)
571 return;
572
573 effective_connection_type_ = effective_connection_type;
574 UpdateParamsForNetworkQuality();
575
576 // Change in network quality may allow |this| to dispatch more requests.
577 LoadAnyStartablePendingRequests(
578 RequestStartTrigger::EFFECTIVE_CONNECTION_TYPE_CHANGED);
579 }
580
581 // net::PeerToPeerConnectionsCountObserver:
OnPeerToPeerConnectionsCountChange(uint32_t count)582 void OnPeerToPeerConnectionsCountChange(uint32_t count) override {
583 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
584
585 if (p2p_connections_count_ == count)
586 return;
587
588 if (p2p_connections_count_ > 0 && count == 0) {
589 // If the count of P2P connections went down to 0, start a timer. When the
590 // timer fires, the queued browser-initiated requests would be dispatched.
591 p2p_connections_count_end_timestamp_ = tick_clock_->NowTicks();
592 p2p_connections_count_ended_timer_.Stop();
593 p2p_connections_count_ended_timer_.Start(
594 FROM_HERE,
595 resource_scheduler_->resource_scheduler_params_manager_
596 .TimeToPauseHeavyBrowserInitiatedRequestsAfterEndOfP2PConnections(),
597 this,
598 &ResourceScheduler::Client::OnP2PConnectionsCountEndedTimerFired);
599 }
600
601 p2p_connections_count_ = count;
602
603 if (p2p_connections_count_ > 0 &&
604 !p2p_connections_count_active_timestamp_.has_value()) {
605 p2p_connections_count_active_timestamp_ = base::TimeTicks::Now();
606 }
607
608 if (p2p_connections_count_ == 0 &&
609 p2p_connections_count_active_timestamp_.has_value()) {
610 p2p_connections_count_active_timestamp_ = base::nullopt;
611 }
612
613 LoadAnyStartablePendingRequests(
614 RequestStartTrigger::PEER_TO_PEER_CONNECTIONS_COUNT_CHANGED);
615 }
616
OnP2PConnectionsCountEndedTimerFired()617 void OnP2PConnectionsCountEndedTimerFired() {
618 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
619
620 LoadAnyStartablePendingRequests(
621 RequestStartTrigger::PEER_TO_PEER_CONNECTIONS_COUNT_CHANGED);
622 }
623
624 // Records the metrics related to number of requests in flight.
RecordRequestCountMetrics() const625 void RecordRequestCountMetrics() const {
626 UMA_HISTOGRAM_COUNTS_100("ResourceScheduler.RequestsCount.All",
627 in_flight_requests_.size());
628 UMA_HISTOGRAM_COUNTS_100("ResourceScheduler.RequestsCount.Delayable",
629 in_flight_delayable_count_);
630 UMA_HISTOGRAM_COUNTS_100(
631 "ResourceScheduler.RequestsCount.NonDelayable",
632 in_flight_requests_.size() - in_flight_delayable_count_);
633 UMA_HISTOGRAM_COUNTS_100(
634 "ResourceScheduler.RequestsCount.TotalLayoutBlocking",
635 total_layout_blocking_count_);
636
637 resource_scheduler_->RecordGlobalRequestCountMetrics();
638 }
639
InsertInFlightRequest(ScheduledResourceRequestImpl * request)640 void InsertInFlightRequest(ScheduledResourceRequestImpl* request) {
641 in_flight_requests_.insert(request);
642 SetRequestAttributes(request, DetermineRequestAttributes(request));
643 RecordRequestCountMetrics();
644
645 if (RequestAttributesAreSet(request->attributes(), kAttributeDelayable)) {
646 // Notify all in-flight with the new count of in-flight delayable
647 // requests.
648 for (RequestSet::const_iterator it = in_flight_requests_.begin();
649 it != in_flight_requests_.end(); ++it) {
650 (*it)->UpdateDelayableRequestsInFlight(in_flight_delayable_count_);
651 }
652 }
653
654 if (RequestAttributesAreSet(request->attributes(),
655 kAttributeLayoutBlocking) ||
656 !RequestAttributesAreSet(request->attributes(), kAttributeDelayable)) {
657 // |request| is either a layout blocking or a non-delayable request.
658 request->UpdateDelayableRequestsInFlight(in_flight_delayable_count_);
659 }
660 }
661
EraseInFlightRequest(ScheduledResourceRequestImpl * request)662 void EraseInFlightRequest(ScheduledResourceRequestImpl* request) {
663 size_t erased = in_flight_requests_.erase(request);
664 DCHECK_EQ(1u, erased);
665 // Clear any special state that we were tracking for this request.
666 SetRequestAttributes(request, kAttributeNone);
667 }
668
ClearInFlightRequests()669 void ClearInFlightRequests() {
670 in_flight_requests_.clear();
671 in_flight_delayable_count_ = 0;
672 total_layout_blocking_count_ = 0;
673 }
674
CountRequestsWithAttributes(const RequestAttributes attributes,ScheduledResourceRequestImpl * current_request)675 size_t CountRequestsWithAttributes(
676 const RequestAttributes attributes,
677 ScheduledResourceRequestImpl* current_request) {
678 size_t matching_request_count = 0;
679 for (RequestSet::const_iterator it = in_flight_requests_.begin();
680 it != in_flight_requests_.end(); ++it) {
681 if (RequestAttributesAreSet((*it)->attributes(), attributes))
682 matching_request_count++;
683 }
684 if (!RequestAttributesAreSet(attributes, kAttributeInFlight)) {
685 bool current_request_is_pending = false;
686 for (RequestQueue::NetQueue::const_iterator it =
687 pending_requests_.GetNextHighestIterator();
688 it != pending_requests_.End(); ++it) {
689 if (RequestAttributesAreSet((*it)->attributes(), attributes))
690 matching_request_count++;
691 if (*it == current_request)
692 current_request_is_pending = true;
693 }
694 // Account for the current request if it is not in one of the lists yet.
695 if (current_request &&
696 !base::Contains(in_flight_requests_, current_request) &&
697 !current_request_is_pending) {
698 if (RequestAttributesAreSet(current_request->attributes(), attributes))
699 matching_request_count++;
700 }
701 }
702 return matching_request_count;
703 }
704
RequestAttributesAreSet(RequestAttributes request_attributes,RequestAttributes matching_attributes) const705 bool RequestAttributesAreSet(RequestAttributes request_attributes,
706 RequestAttributes matching_attributes) const {
707 return (request_attributes & matching_attributes) == matching_attributes;
708 }
709
SetRequestAttributes(ScheduledResourceRequestImpl * request,RequestAttributes attributes)710 void SetRequestAttributes(ScheduledResourceRequestImpl* request,
711 RequestAttributes attributes) {
712 RequestAttributes old_attributes = request->attributes();
713 if (old_attributes == attributes)
714 return;
715
716 if (RequestAttributesAreSet(old_attributes,
717 kAttributeInFlight | kAttributeDelayable)) {
718 in_flight_delayable_count_--;
719 }
720 if (RequestAttributesAreSet(old_attributes, kAttributeLayoutBlocking))
721 total_layout_blocking_count_--;
722
723 if (RequestAttributesAreSet(attributes,
724 kAttributeInFlight | kAttributeDelayable)) {
725 in_flight_delayable_count_++;
726 }
727 if (RequestAttributesAreSet(attributes, kAttributeLayoutBlocking))
728 total_layout_blocking_count_++;
729
730 request->set_attributes(attributes);
731 DCHECK_EQ(CountRequestsWithAttributes(
732 kAttributeInFlight | kAttributeDelayable, request),
733 in_flight_delayable_count_);
734 DCHECK_EQ(CountRequestsWithAttributes(kAttributeLayoutBlocking, request),
735 total_layout_blocking_count_);
736 }
737
DetermineRequestAttributes(ScheduledResourceRequestImpl * request)738 RequestAttributes DetermineRequestAttributes(
739 ScheduledResourceRequestImpl* request) {
740 RequestAttributes attributes = kAttributeNone;
741
742 if (base::Contains(in_flight_requests_, request))
743 attributes |= kAttributeInFlight;
744
745 if (RequestAttributesAreSet(request->attributes(),
746 kAttributeLayoutBlocking)) {
747 // If a request is already marked as layout-blocking make sure to keep the
748 // attribute across redirects.
749 attributes |= kAttributeLayoutBlocking;
750 } else if (request->url_request()->priority() <
751 kDelayablePriorityThreshold) {
752 if (params_for_network_quality_
753 .delay_requests_on_multiplexed_connections) {
754 // Resources below the delayable priority threshold that are considered
755 // delayable.
756 attributes |= kAttributeDelayable;
757 } else {
758 // Resources below the delayable priority threshold that are being
759 // requested from a server that does not support native prioritization
760 // are considered delayable.
761 url::SchemeHostPort scheme_host_port(request->url_request()->url());
762 net::HttpServerProperties& http_server_properties =
763 *request->url_request()->context()->http_server_properties();
764 if (!http_server_properties.SupportsRequestPriority(
765 scheme_host_port, request->url_request()
766 ->isolation_info()
767 .network_isolation_key())) {
768 attributes |= kAttributeDelayable;
769 }
770 }
771 }
772
773 return attributes;
774 }
775
ReachedMaxRequestsPerHostPerClient(const net::HostPortPair & active_request_host,bool supports_priority) const776 bool ReachedMaxRequestsPerHostPerClient(
777 const net::HostPortPair& active_request_host,
778 bool supports_priority) const {
779 // This method should not be called for requests to origins that support
780 // prioritization (aka multiplexing) unless one of the experiments to
781 // throttle priority requests is enabled.
782 DCHECK(
783 !supports_priority ||
784 params_for_network_quality_.delay_requests_on_multiplexed_connections);
785
786 // kMaxNumDelayableRequestsPerHostPerClient limit does not apply to servers
787 // that support request priorities when
788 // |delay_requests_on_multiplexed_connections| is true. If
789 // |delay_requests_on_multiplexed_connections| is false, then
790 // kMaxNumDelayableRequestsPerHostPerClient limit still applies to other
791 // experiments that delay priority requests.
792 if (supports_priority &&
793 params_for_network_quality_.delay_requests_on_multiplexed_connections) {
794 return false;
795 }
796
797 size_t same_host_count = 0;
798 for (RequestSet::const_iterator it = in_flight_requests_.begin();
799 it != in_flight_requests_.end(); ++it) {
800 if (active_request_host.Equals((*it)->host_port_pair())) {
801 same_host_count++;
802 if (same_host_count >= kMaxNumDelayableRequestsPerHostPerClient)
803 return true;
804 }
805 }
806 return false;
807 }
808
RecordMetricsOnStartRequest(const ScheduledResourceRequestImpl & request,base::TimeTicks ticks_now) const809 void RecordMetricsOnStartRequest(const ScheduledResourceRequestImpl& request,
810 base::TimeTicks ticks_now) const {
811 const size_t non_delayable_requests_in_flight_count =
812 in_flight_requests_.size() - in_flight_delayable_count_;
813
814 // Record the number of delayable requests in-flight when a non-delayable
815 // request starts.
816 if (!RequestAttributesAreSet(request.attributes(), kAttributeDelayable)) {
817 if (non_delayable_requests_in_flight_count > 0) {
818 if (last_non_delayable_request_start_) {
819 UMA_HISTOGRAM_MEDIUM_TIMES(
820 "ResourceScheduler.NonDelayableLastStartToNonDelayableStart."
821 "NonDelayableInFlight",
822 ticks_now - last_non_delayable_request_start_.value());
823 }
824 } else {
825 if (last_non_delayable_request_end_) {
826 UMA_HISTOGRAM_MEDIUM_TIMES(
827 "ResourceScheduler.NonDelayableLastEndToNonDelayableStart."
828 "NonDelayableNotInFlight",
829 ticks_now - last_non_delayable_request_end_.value());
830 }
831 }
832
833 UMA_HISTOGRAM_COUNTS_100(
834 "ResourceScheduler.NumDelayableRequestsInFlightAtStart.NonDelayable",
835 in_flight_delayable_count_);
836 if (last_non_delayable_request_start_.has_value()) {
837 UMA_HISTOGRAM_MEDIUM_TIMES(
838 "ResourceScheduler.NonDelayableLastStartToNonDelayableStart",
839 ticks_now - last_non_delayable_request_start_.value());
840 }
841 if (last_non_delayable_request_end_.has_value()) {
842 UMA_HISTOGRAM_MEDIUM_TIMES(
843 "ResourceScheduler.NonDelayableLastEndToNonDelayableStart",
844 ticks_now - last_non_delayable_request_end_.value());
845 }
846
847 // Record time since last non-delayable request start or end, whichever
848 // happened later.
849 base::Optional<base::TimeTicks> last_non_delayable_request_start_or_end;
850 if (last_non_delayable_request_start_.has_value() &&
851 !last_non_delayable_request_end_.has_value()) {
852 last_non_delayable_request_start_or_end =
853 last_non_delayable_request_start_;
854 } else if (!last_non_delayable_request_start_.has_value() &&
855 last_non_delayable_request_end_.has_value()) {
856 last_non_delayable_request_start_or_end =
857 last_non_delayable_request_end_;
858 } else if (last_non_delayable_request_start_.has_value() &&
859 last_non_delayable_request_end_.has_value()) {
860 last_non_delayable_request_start_or_end =
861 std::max(last_non_delayable_request_start_.value(),
862 last_non_delayable_request_end_.value());
863 }
864
865 if (last_non_delayable_request_start_or_end) {
866 UMA_HISTOGRAM_MEDIUM_TIMES(
867 "ResourceScheduler.NonDelayableLastStartOrEndToNonDelayableStart",
868 ticks_now - last_non_delayable_request_start_or_end.value());
869 }
870 }
871 }
872
StartRequest(ScheduledResourceRequestImpl * request,StartMode start_mode,RequestStartTrigger trigger)873 void StartRequest(ScheduledResourceRequestImpl* request,
874 StartMode start_mode,
875 RequestStartTrigger trigger) {
876 const base::TimeTicks ticks_now = tick_clock_->NowTicks();
877 // Only log on requests that were blocked by the ResourceScheduler.
878 if (start_mode == START_ASYNC) {
879 DCHECK_NE(RequestStartTrigger::NONE, trigger);
880 request->url_request()->net_log().AddEventWithStringParams(
881 net::NetLogEventType::RESOURCE_SCHEDULER_REQUEST_STARTED, "trigger",
882 RequestStartTriggerString(trigger));
883 }
884 if (request)
885 RecordMetricsOnStartRequest(*request, ticks_now);
886
887 DCHECK(!request->url_request()->creation_time().is_null());
888 base::TimeDelta queuing_duration =
889 ticks_now - request->url_request()->creation_time();
890 base::UmaHistogramMediumTimes(
891 "ResourceScheduler.RequestQueuingDuration.Priority" +
892 base::NumberToString(
893 request->get_request_priority_params().priority),
894 queuing_duration);
895
896 // Update the start time of the non-delayble request.
897 if (!RequestAttributesAreSet(request->attributes(), kAttributeDelayable))
898 last_non_delayable_request_start_ = ticks_now;
899
900 InsertInFlightRequest(request);
901 request->Start(start_mode);
902 }
903
904 // Returns true if |request| should be throttled to avoid network contention
905 // with active P2P connections.
ShouldThrottleBrowserInitiatedRequestDueToP2PConnections(const ScheduledResourceRequestImpl & request) const906 bool ShouldThrottleBrowserInitiatedRequestDueToP2PConnections(
907 const ScheduledResourceRequestImpl& request) const {
908 DCHECK(is_browser_client_);
909
910 if (!base::FeatureList::IsEnabled(
911 features::kPauseBrowserInitiatedHeavyTrafficForP2P)) {
912 return false;
913 }
914
915 if (p2p_connections_count_ == 0) {
916 // If the current count of P2P connections is 0, then check when was the
917 // last time when there was an active P2P connection. If that timestamp is
918 // recent, it's a heuristic indication that a new P2P connection may
919 // start soon. To avoid network contention for that connection, keep
920 // throttling browser-initiated requests.
921 if (!p2p_connections_count_end_timestamp_.has_value())
922 return false;
923
924 bool p2p_connections_ended_long_back =
925 (tick_clock_->NowTicks() -
926 p2p_connections_count_end_timestamp_.value()) >=
927 resource_scheduler_->resource_scheduler_params_manager_
928 .TimeToPauseHeavyBrowserInitiatedRequestsAfterEndOfP2PConnections();
929 if (p2p_connections_ended_long_back)
930 return false;
931
932 // If there are no currently active P2P connections, and the last
933 // connection ended recently, then |p2p_connections_count_ended_timer_|
934 // must be running. When |p2p_connections_count_ended_timer_| fires,
935 // queued browser-initiated requests would be dispatched.
936 DCHECK(p2p_connections_count_ended_timer_.IsRunning());
937 }
938
939 // Only throttle when effective connection type is between Slow2G and 3G.
940 if (effective_connection_type_ <= net::EFFECTIVE_CONNECTION_TYPE_OFFLINE ||
941 effective_connection_type_ >= net::EFFECTIVE_CONNECTION_TYPE_4G) {
942 return false;
943 }
944
945 if (request.url_request()->priority() == net::HIGHEST)
946 return false;
947
948 if (p2p_connections_count_ > 0) {
949 base::TimeDelta time_since_p2p_connections_active =
950 tick_clock_->NowTicks() -
951 p2p_connections_count_active_timestamp_.value();
952
953 base::Optional<base::TimeDelta> max_wait_time_p2p_connections =
954 resource_scheduler_->resource_scheduler_params_manager_
955 .max_wait_time_p2p_connections();
956
957 if (time_since_p2p_connections_active >
958 max_wait_time_p2p_connections.value()) {
959 return false;
960 }
961 }
962
963 // Check other request specific constraints.
964 const net::NetworkTrafficAnnotationTag& traffic_annotation =
965 request.url_request()->traffic_annotation();
966 const int32_t unique_id_hash_code = traffic_annotation.unique_id_hash_code;
967
968 if (!resource_scheduler_->resource_scheduler_params_manager_
969 .CanThrottleNetworkTrafficAnnotationHash(unique_id_hash_code)) {
970 return false;
971 }
972
973 return true;
974 }
975
976 // Records metrics on browser initiated requests. Called when the request is
977 // dispatched to the network.
RecordMetricsForBrowserInitiatedRequestsOnNetworkDispatch(const ScheduledResourceRequestImpl & request) const978 void RecordMetricsForBrowserInitiatedRequestsOnNetworkDispatch(
979 const ScheduledResourceRequestImpl& request) const {
980 const net::NetworkTrafficAnnotationTag& traffic_annotation =
981 request.url_request()->traffic_annotation();
982 const int32_t unique_id_hash_code = traffic_annotation.unique_id_hash_code;
983
984 // Metrics are recorded only for browser initiated requests that are
985 // eligible for throttling.
986 if (!resource_scheduler_->resource_scheduler_params_manager_
987 .CanThrottleNetworkTrafficAnnotationHash(unique_id_hash_code)) {
988 return;
989 }
990
991 base::TimeDelta queuing_duration =
992 tick_clock_->NowTicks() - request.url_request()->creation_time();
993 UMA_HISTOGRAM_LONG_TIMES(
994 "ResourceScheduler.BrowserInitiatedHeavyRequest.QueuingDuration",
995 queuing_duration);
996 }
997
998 // ShouldStartRequest is the main scheduling algorithm.
999 //
1000 // Requests are evaluated on five attributes:
1001 //
1002 // 1. Non-delayable requests:
1003 // * Synchronous requests.
1004 // * Non-HTTP[S] requests.
1005 //
1006 // 2. Requests to request-priority-capable origin servers.
1007 //
1008 // 3. High-priority requests:
1009 // * Higher priority requests (>= net::LOW).
1010 //
1011 // 4. Layout-blocking requests:
1012 // * High-priority requests (> net::LOW) initiated before the renderer has
1013 // a <body>.
1014 //
1015 // 5. Low priority requests
1016 //
1017 // The following rules are followed:
1018 //
1019 // All types of requests:
1020 // * Non-delayable, High-priority and request-priority capable requests are
1021 // issued immediately.
1022 // * Low priority requests are delayable.
1023 // * While kInFlightNonDelayableRequestCountPerClientThreshold
1024 // layout-blocking requests are loading or the body tag has not yet been
1025 // parsed, limit the number of delayable requests that may be in flight
1026 // to kMaxNumDelayableWhileLayoutBlockingPerClient.
1027 // * If no high priority or layout-blocking requests are in flight, start
1028 // loading delayable requests.
1029 // * Never exceed 10 delayable requests in flight per client.
1030 // * Never exceed 6 delayable requests for a given host.
1031
ShouldStartRequest(ScheduledResourceRequestImpl * request) const1032 ShouldStartReqResult ShouldStartRequest(
1033 ScheduledResourceRequestImpl* request) const {
1034 // Browser requests are treated differently since they are not user-facing.
1035 if (is_browser_client_) {
1036 if (ShouldThrottleBrowserInitiatedRequestDueToP2PConnections(*request)) {
1037 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING;
1038 }
1039
1040 RecordMetricsForBrowserInitiatedRequestsOnNetworkDispatch(*request);
1041 return START_REQUEST;
1042 }
1043
1044 const net::URLRequest& url_request = *request->url_request();
1045 // Syncronous requests could block the entire render, which could impact
1046 // user-observable Clients.
1047 if (!request->is_async())
1048 return START_REQUEST;
1049
1050 // TODO(simonjam): This may end up causing disk contention. We should
1051 // experiment with throttling if that happens.
1052 if (!url_request.url().SchemeIsHTTPOrHTTPS())
1053 return START_REQUEST;
1054
1055 if (params_for_network_quality_.max_queuing_time &&
1056 tick_clock_->NowTicks() - url_request.creation_time() >=
1057 params_for_network_quality_.max_queuing_time) {
1058 return START_REQUEST;
1059 }
1060
1061 const net::HostPortPair& host_port_pair = request->host_port_pair();
1062
1063 bool priority_delayable =
1064 params_for_network_quality_.delay_requests_on_multiplexed_connections;
1065
1066 url::SchemeHostPort scheme_host_port(url_request.url());
1067 bool supports_priority =
1068 url_request.context()
1069 ->http_server_properties()
1070 ->SupportsRequestPriority(
1071 scheme_host_port,
1072 url_request.isolation_info().network_isolation_key());
1073
1074 if (!priority_delayable) {
1075 // TODO(willchan): We should really improve this algorithm as described in
1076 // https://crbug.com/164101. Also, theoretically we should not count a
1077 // request-priority capable request against the delayable requests limit.
1078 if (supports_priority)
1079 return START_REQUEST;
1080 }
1081
1082 // Non-delayable requests.
1083 if (!RequestAttributesAreSet(request->attributes(), kAttributeDelayable))
1084 return START_REQUEST;
1085
1086 // Delayable requests.
1087 DCHECK_GE(in_flight_requests_.size(), in_flight_delayable_count_);
1088 size_t num_non_delayable_requests_weighted = static_cast<size_t>(
1089 params_for_network_quality_.non_delayable_weight *
1090 (in_flight_requests_.size() - in_flight_delayable_count_));
1091 if ((in_flight_delayable_count_ + num_non_delayable_requests_weighted >=
1092 params_for_network_quality_.max_delayable_requests)) {
1093 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
1094 }
1095
1096 if (ReachedMaxRequestsPerHostPerClient(host_port_pair, supports_priority)) {
1097 // There may be other requests for other hosts that may be allowed,
1098 // so keep checking.
1099 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING;
1100 }
1101
1102 // The in-flight requests consist of layout-blocking requests,
1103 // normal requests and delayable requests. Everything except for
1104 // delayable requests is handled above here so this is deciding what to
1105 // do with a delayable request while we are in the layout-blocking phase
1106 // of loading.
1107 if (total_layout_blocking_count_ != 0) {
1108 size_t non_delayable_requests_in_flight_count =
1109 in_flight_requests_.size() - in_flight_delayable_count_;
1110 if (non_delayable_requests_in_flight_count >
1111 kInFlightNonDelayableRequestCountPerClientThreshold) {
1112 // Too many higher priority in-flight requests to allow lower priority
1113 // requests through.
1114 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
1115 }
1116 if (in_flight_requests_.size() > 0 &&
1117 (in_flight_delayable_count_ >=
1118 kMaxNumDelayableWhileLayoutBlockingPerClient)) {
1119 // Block the request if at least one request is in flight and the
1120 // number of in-flight delayable requests has hit the configured
1121 // limit.
1122 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
1123 }
1124 }
1125
1126 if (IsNonDelayableRequestAnticipated()) {
1127 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
1128 }
1129
1130 return START_REQUEST;
1131 }
1132
1133 // Returns true if a non-delayable request is expected to arrive soon.
IsNonDelayableRequestAnticipated() const1134 bool IsNonDelayableRequestAnticipated() const {
1135 base::Optional<double> http_rtt_multiplier =
1136 params_for_network_quality_
1137 .http_rtt_multiplier_for_proactive_throttling;
1138
1139 if (!http_rtt_multiplier.has_value())
1140 return false;
1141
1142 if (http_rtt_multiplier <= 0)
1143 return false;
1144
1145 // Currently, the heuristic for predicting the arrival of a non-delayable
1146 // request makes a prediction only if a non-delayable request has started
1147 // previously in this resource scheduler client.
1148 if (!last_non_delayable_request_start_.has_value())
1149 return false;
1150
1151 base::Optional<base::TimeDelta> http_rtt =
1152 network_quality_estimator_->GetHttpRTT();
1153 if (!http_rtt.has_value())
1154 return false;
1155
1156 base::TimeDelta threshold_for_proactive_throttling =
1157 http_rtt.value() * http_rtt_multiplier.value();
1158 base::TimeDelta time_since_last_non_delayable_request_start =
1159 tick_clock_->NowTicks() - last_non_delayable_request_start_.value();
1160
1161 if (time_since_last_non_delayable_request_start >=
1162 threshold_for_proactive_throttling) {
1163 // Last non-delayable request started more than
1164 // |threshold_for_proactive_throttling| back. The algorithm estimates that
1165 // by this time any non-delayable that were triggered by requests that
1166 // started long time back would have arrived at resource scheduler by now.
1167 //
1168 // On the other hand, if the last non-delayable request started recently,
1169 // then it's likely that the parsing of the response from that recently
1170 // started request would trigger additional non-delayable requests.
1171 return false;
1172 }
1173 return true;
1174 }
1175
1176 // It is common for a burst of messages to come from the renderer which
1177 // trigger starting pending requests. Naively, this would result in O(n*m)
1178 // behavior for n pending requests and m <= n messages, as
1179 // LoadAnyStartablePendingRequest is O(n) for n pending requests. To solve
1180 // this, just post a task to the end of the queue to call the method,
1181 // coalescing the m messages into a single call to
1182 // LoadAnyStartablePendingRequests.
1183 // TODO(csharrison): Reconsider this if IPC batching becomes an easy to use
1184 // pattern.
ScheduleLoadAnyStartablePendingRequests(RequestStartTrigger trigger)1185 void ScheduleLoadAnyStartablePendingRequests(RequestStartTrigger trigger) {
1186 if (num_skipped_scans_due_to_scheduled_start_ == 0) {
1187 TRACE_EVENT0("loading", "ScheduleLoadAnyStartablePendingRequests");
1188 resource_scheduler_->task_runner()->PostTask(
1189 FROM_HERE, base::BindOnce(&Client::LoadAnyStartablePendingRequests,
1190 weak_ptr_factory_.GetWeakPtr(), trigger));
1191 }
1192 num_skipped_scans_due_to_scheduled_start_ += 1;
1193 }
1194
LoadAnyStartablePendingRequests(RequestStartTrigger trigger)1195 void LoadAnyStartablePendingRequests(RequestStartTrigger trigger) {
1196 // We iterate through all the pending requests, starting with the highest
1197 // priority one. For each entry, one of three things can happen:
1198 // 1) We start the request, remove it from the list, and keep checking.
1199 // 2) We do NOT start the request, but ShouldStartRequest() signals us that
1200 // there may be room for other requests, so we keep checking and leave
1201 // the previous request still in the list.
1202 // 3) We do not start the request, same as above, but StartRequest() tells
1203 // us there's no point in checking any further requests.
1204 TRACE_EVENT0("loading", "LoadAnyStartablePendingRequests");
1205 if (num_skipped_scans_due_to_scheduled_start_ > 0) {
1206 UMA_HISTOGRAM_COUNTS_1M("ResourceScheduler.NumSkippedScans.ScheduleStart",
1207 num_skipped_scans_due_to_scheduled_start_);
1208 }
1209 num_skipped_scans_due_to_scheduled_start_ = 0;
1210 RequestQueue::NetQueue::iterator request_iter =
1211 pending_requests_.GetNextHighestIterator();
1212
1213 while (request_iter != pending_requests_.End()) {
1214 ScheduledResourceRequestImpl* request = *request_iter;
1215 ShouldStartReqResult query_result = ShouldStartRequest(request);
1216
1217 if (query_result == START_REQUEST) {
1218 pending_requests_.Erase(request);
1219 StartRequest(request, START_ASYNC, trigger);
1220
1221 // StartRequest can modify the pending list, so we (re)start evaluation
1222 // from the currently highest priority request. Avoid copying a singular
1223 // iterator, which would trigger undefined behavior.
1224 if (pending_requests_.GetNextHighestIterator() ==
1225 pending_requests_.End())
1226 break;
1227 request_iter = pending_requests_.GetNextHighestIterator();
1228 } else if (query_result == DO_NOT_START_REQUEST_AND_KEEP_SEARCHING) {
1229 ++request_iter;
1230 continue;
1231 } else {
1232 DCHECK(query_result == DO_NOT_START_REQUEST_AND_STOP_SEARCHING);
1233 break;
1234 }
1235 }
1236 }
1237
1238 // If |request| was delayable, this method records how long after |request|
1239 // started, a non-delayable request also started. This is the duration of time
1240 // that |request| should have been queued for so as to avoid any network
1241 // contention with all later-arriving non-delayable requests. Must be called
1242 // after |request| is finished.
RecordNetworkContentionMetrics(const ScheduledResourceRequestImpl & request) const1243 void RecordNetworkContentionMetrics(
1244 const ScheduledResourceRequestImpl& request) const {
1245 if (!RequestAttributesAreSet(request.attributes(), kAttributeDelayable))
1246 return;
1247
1248 base::TimeDelta ideal_duration_to_wait;
1249 if (!last_non_delayable_request_start_) {
1250 // No non-delayable request has been started in this client so far.
1251 // |request| did not have to wait at all to avoid network contention.
1252 ideal_duration_to_wait = base::TimeDelta();
1253 } else if (request.url_request()->creation_time() >
1254 last_non_delayable_request_start_) {
1255 // Last non-delayable request in this client started before |request|
1256 // was created. |request| did not have to wait at all to avoid network
1257 // contention with non-delayable requests.
1258 ideal_duration_to_wait = base::TimeDelta();
1259 } else {
1260 // The latest non-delayable request started at
1261 // |last_non_delayable_request_start_| which happened after the
1262 // creation of |request|.
1263 ideal_duration_to_wait = last_non_delayable_request_start_.value() -
1264 request.url_request()->creation_time();
1265 }
1266
1267 UMA_HISTOGRAM_MEDIUM_TIMES(
1268 "ResourceScheduler.DelayableRequests."
1269 "WaitTimeToAvoidContentionWithNonDelayableRequest",
1270 ideal_duration_to_wait);
1271 }
1272
1273 // Tracks if the main HTML parser has reached the body which marks the end of
1274 // layout-blocking resources.
1275 // This is disabled and the is always true when kRendererSideResourceScheduler
1276 // is enabled.
1277 RequestQueue pending_requests_;
1278 RequestSet in_flight_requests_;
1279
1280 // True if |this| client is created for browser initiated requests.
1281 const bool is_browser_client_;
1282
1283 // The number of delayable in-flight requests.
1284 size_t in_flight_delayable_count_;
1285 // The number of layout-blocking in-flight requests.
1286 size_t total_layout_blocking_count_;
1287
1288 // The number of LoadAnyStartablePendingRequests scans that were skipped due
1289 // to smarter task scheduling around reprioritization.
1290 int num_skipped_scans_due_to_scheduled_start_;
1291
1292 // Network quality estimator for network aware resource scheudling. This may
1293 // be null.
1294 net::NetworkQualityEstimator* network_quality_estimator_;
1295
1296 // Resource scheduling params computed for the current network quality.
1297 // These are recomputed every time an |OnNavigate| event is triggered.
1298 ResourceSchedulerParamsManager::ParamsForNetworkQuality
1299 params_for_network_quality_;
1300
1301 // A pointer to the resource scheduler which contains the resource scheduling
1302 // configuration.
1303 ResourceScheduler* resource_scheduler_;
1304
1305 // Guaranteed to be non-null.
1306 const base::TickClock* tick_clock_;
1307
1308 // Time when the last non-delayble request started in this client.
1309 base::Optional<base::TimeTicks> last_non_delayable_request_start_;
1310
1311 // Time when the last non-delayble request ended in this client.
1312 base::Optional<base::TimeTicks> last_non_delayable_request_end_;
1313
1314 // Current estimated value of the effective connection type.
1315 net::EffectiveConnectionType effective_connection_type_ =
1316 net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
1317
1318 // Current count of active peer to peer connections.
1319 uint32_t p2p_connections_count_ = 0u;
1320
1321 // Earliest timestamp since when there is at least one active peer to peer
1322 // connection. Set to current timestamp when |p2p_connections_count_|
1323 // changes from 0 to a non-zero value. Reset to null when
1324 // |p2p_connections_count_| becomes 0.
1325 base::Optional<base::TimeTicks> p2p_connections_count_active_timestamp_;
1326
1327 // Earliest timestamp since when the count of active peer to peer
1328 // connection counts dropped from a non-zero value to zero. Set to current
1329 // timestamp when |p2p_connections_count_| changes from a non-zero value to 0.
1330 base::Optional<base::TimeTicks> p2p_connections_count_end_timestamp_;
1331
1332 base::OneShotTimer p2p_connections_count_ended_timer_;
1333
1334 SEQUENCE_CHECKER(sequence_checker_);
1335
1336 base::WeakPtrFactory<ResourceScheduler::Client> weak_ptr_factory_{this};
1337 };
1338
ResourceScheduler(const base::TickClock * tick_clock)1339 ResourceScheduler::ResourceScheduler(const base::TickClock* tick_clock)
1340 : tick_clock_(tick_clock ? tick_clock
1341 : base::DefaultTickClock::GetInstance()),
1342 queued_requests_dispatch_periodicity_(
1343 GetQueuedRequestsDispatchPeriodicity()),
1344 task_runner_(base::ThreadTaskRunnerHandle::Get()) {
1345 DCHECK(tick_clock_);
1346
1347 StartLongQueuedRequestsDispatchTimerIfNeeded();
1348 }
1349
~ResourceScheduler()1350 ResourceScheduler::~ResourceScheduler() {
1351 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1352 DCHECK(unowned_requests_.empty());
1353 DCHECK(client_map_.empty());
1354 }
1355
1356 std::unique_ptr<ResourceScheduler::ScheduledResourceRequest>
ScheduleRequest(int child_id,int route_id,bool is_async,net::URLRequest * url_request)1357 ResourceScheduler::ScheduleRequest(int child_id,
1358 int route_id,
1359 bool is_async,
1360 net::URLRequest* url_request) {
1361 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1362 ClientId client_id = MakeClientId(child_id, route_id);
1363 std::unique_ptr<ScheduledResourceRequestImpl> request(
1364 new ScheduledResourceRequestImpl(
1365 client_id, url_request, this,
1366 RequestPriorityParams(url_request->priority(), 0), is_async));
1367
1368 ClientMap::iterator it = client_map_.find(client_id);
1369 if (it == client_map_.end()) {
1370 // There are several ways this could happen:
1371 // 1. <a ping> requests don't have a route_id.
1372 // 2. Most unittests don't send the IPCs needed to register Clients.
1373 // 3. The tab is closed while a RequestResource IPC is in flight.
1374 unowned_requests_.insert(request.get());
1375 request->Start(START_SYNC);
1376 return std::move(request);
1377 }
1378
1379 Client* client = it->second.get();
1380 client->ScheduleRequest(*url_request, request.get());
1381
1382 if (!IsLongQueuedRequestsDispatchTimerRunning())
1383 StartLongQueuedRequestsDispatchTimerIfNeeded();
1384
1385 return std::move(request);
1386 }
1387
RemoveRequest(ScheduledResourceRequestImpl * request)1388 void ResourceScheduler::RemoveRequest(ScheduledResourceRequestImpl* request) {
1389 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1390 if (base::Contains(unowned_requests_, request)) {
1391 unowned_requests_.erase(request);
1392 return;
1393 }
1394
1395 ClientMap::iterator client_it = client_map_.find(request->client_id());
1396 if (client_it == client_map_.end())
1397 return;
1398
1399 Client* client = client_it->second.get();
1400 client->RemoveRequest(request);
1401 }
1402
OnClientCreated(int child_id,int route_id,net::NetworkQualityEstimator * network_quality_estimator)1403 void ResourceScheduler::OnClientCreated(
1404 int child_id,
1405 int route_id,
1406 net::NetworkQualityEstimator* network_quality_estimator) {
1407 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1408 ClientId client_id = MakeClientId(child_id, route_id);
1409 DCHECK(!base::Contains(client_map_, client_id));
1410
1411 client_map_[client_id] =
1412 std::make_unique<Client>(child_id == mojom::kBrowserProcessId,
1413 network_quality_estimator, this, tick_clock_);
1414
1415 UMA_HISTOGRAM_COUNTS_100("ResourceScheduler.ActiveSchedulerClientsCount",
1416 ActiveSchedulerClientsCounter());
1417 }
1418
OnClientDeleted(int child_id,int route_id)1419 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) {
1420 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1421
1422 ClientId client_id = MakeClientId(child_id, route_id);
1423 ClientMap::iterator it = client_map_.find(client_id);
1424 // TODO(crbug.com/873959): Turns this CHECK to DCHECK once the investigation
1425 // is done.
1426 CHECK(it != client_map_.end());
1427
1428 Client* client = it->second.get();
1429 // TODO(crbug.com/873959): Remove this CHECK once the investigation is done.
1430 CHECK(client);
1431 DCHECK(client->HasNoPendingRequests() ||
1432 IsLongQueuedRequestsDispatchTimerRunning());
1433 // ResourceDispatcherHost cancels all requests except for cross-renderer
1434 // navigations, async revalidations and detachable requests after
1435 // OnClientDeleted() returns.
1436 RequestSet client_unowned_requests = client->StartAndRemoveAllRequests();
1437 for (RequestSet::iterator request_it = client_unowned_requests.begin();
1438 request_it != client_unowned_requests.end(); ++request_it) {
1439 unowned_requests_.insert(*request_it);
1440 }
1441
1442 client_map_.erase(it);
1443 }
1444
ActiveSchedulerClientsCounter() const1445 size_t ResourceScheduler::ActiveSchedulerClientsCounter() const {
1446 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1447
1448 size_t active_scheduler_clients_count = 0;
1449 for (const auto& client : client_map_) {
1450 if (client.second->IsActiveResourceSchedulerClient()) {
1451 ++active_scheduler_clients_count;
1452 }
1453 }
1454 return active_scheduler_clients_count;
1455 }
1456
1457 // Records the metrics related to number of requests in flight that are observed
1458 // by the global resource scheduler.
RecordGlobalRequestCountMetrics() const1459 void ResourceScheduler::RecordGlobalRequestCountMetrics() const {
1460 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1461
1462 size_t global_delayable_count = 0;
1463 size_t global_non_delayable_count = 0;
1464 size_t global_layout_blocking_count = 0;
1465
1466 for (const auto& client : client_map_) {
1467 global_delayable_count += client.second->CountInflightDelayableRequests();
1468 global_non_delayable_count +=
1469 client.second->CountInflightNonDelayableRequests();
1470 global_layout_blocking_count +=
1471 client.second->CountInflightLayoutBlockingRequests();
1472 }
1473
1474 UMA_HISTOGRAM_COUNTS_100("ResourceScheduler.RequestsCount.GlobalAll",
1475 global_delayable_count + global_non_delayable_count);
1476 UMA_HISTOGRAM_COUNTS_100("ResourceScheduler.RequestsCount.GlobalDelayable",
1477 global_delayable_count);
1478 UMA_HISTOGRAM_COUNTS_100("ResourceScheduler.RequestsCount.GlobalNonDelayable",
1479 global_non_delayable_count);
1480 UMA_HISTOGRAM_COUNTS_100(
1481 "ResourceScheduler.RequestsCount.GlobalLayoutBlocking",
1482 global_layout_blocking_count);
1483 }
1484
GetClient(int child_id,int route_id)1485 ResourceScheduler::Client* ResourceScheduler::GetClient(int child_id,
1486 int route_id) {
1487 ClientId client_id = MakeClientId(child_id, route_id);
1488 ClientMap::iterator client_it = client_map_.find(client_id);
1489 if (client_it == client_map_.end())
1490 return nullptr;
1491 return client_it->second.get();
1492 }
1493
StartLongQueuedRequestsDispatchTimerIfNeeded()1494 void ResourceScheduler::StartLongQueuedRequestsDispatchTimerIfNeeded() {
1495 bool pending_request_found = false;
1496 for (const auto& client : client_map_) {
1497 if (!client.second->HasNoPendingRequests()) {
1498 pending_request_found = true;
1499 break;
1500 }
1501 }
1502
1503 // If there are no pending requests, then do not start the timer. This ensures
1504 // that we are not running the periodic timer when Chrome is not being
1505 // actively used (e.g., it's in background).
1506 if (!pending_request_found)
1507 return;
1508
1509 long_queued_requests_dispatch_timer_.Start(
1510 FROM_HERE, queued_requests_dispatch_periodicity_, this,
1511 &ResourceScheduler::OnLongQueuedRequestsDispatchTimerFired);
1512 }
1513
OnLongQueuedRequestsDispatchTimerFired()1514 void ResourceScheduler::OnLongQueuedRequestsDispatchTimerFired() {
1515 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1516
1517 for (auto& client : client_map_)
1518 client.second->OnLongQueuedRequestsDispatchTimerFired();
1519
1520 StartLongQueuedRequestsDispatchTimerIfNeeded();
1521 }
1522
ReprioritizeRequest(net::URLRequest * request,net::RequestPriority new_priority,int new_intra_priority_value)1523 void ResourceScheduler::ReprioritizeRequest(net::URLRequest* request,
1524 net::RequestPriority new_priority,
1525 int new_intra_priority_value) {
1526 if (request->load_flags() & net::LOAD_IGNORE_LIMITS) {
1527 // Requests with the IGNORE_LIMITS flag must stay at MAXIMUM_PRIORITY.
1528 return;
1529 }
1530
1531 auto* scheduled_resource_request =
1532 ScheduledResourceRequestImpl::ForRequest(request);
1533
1534 // Downloads don't use the resource scheduler.
1535 if (!scheduled_resource_request) {
1536 request->SetPriority(new_priority);
1537 return;
1538 }
1539
1540 RequestPriorityParams new_priority_params(new_priority,
1541 new_intra_priority_value);
1542 RequestPriorityParams old_priority_params =
1543 scheduled_resource_request->get_request_priority_params();
1544
1545 if (old_priority_params == new_priority_params)
1546 return;
1547
1548 ClientMap::iterator client_it =
1549 client_map_.find(scheduled_resource_request->client_id());
1550 if (client_it == client_map_.end()) {
1551 // The client was likely deleted shortly before we received this IPC.
1552 request->SetPriority(new_priority_params.priority);
1553 scheduled_resource_request->set_request_priority_params(
1554 new_priority_params);
1555 return;
1556 }
1557
1558 Client* client = client_it->second.get();
1559 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params,
1560 new_priority_params);
1561 }
1562
ReprioritizeRequest(net::URLRequest * request,net::RequestPriority new_priority)1563 void ResourceScheduler::ReprioritizeRequest(net::URLRequest* request,
1564 net::RequestPriority new_priority) {
1565 int current_intra_priority = 0;
1566 auto* existing_request = ScheduledResourceRequestImpl::ForRequest(request);
1567 if (existing_request) {
1568 current_intra_priority =
1569 existing_request->get_request_priority_params().intra_priority;
1570 }
1571 ReprioritizeRequest(request, new_priority, current_intra_priority);
1572 }
1573
MakeClientId(int child_id,int route_id) const1574 ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
1575 int child_id,
1576 int route_id) const {
1577 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id;
1578 }
1579
IsLongQueuedRequestsDispatchTimerRunning() const1580 bool ResourceScheduler::IsLongQueuedRequestsDispatchTimerRunning() const {
1581 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1582 return long_queued_requests_dispatch_timer_.IsRunning();
1583 }
1584
task_runner()1585 base::SequencedTaskRunner* ResourceScheduler::task_runner() {
1586 return task_runner_.get();
1587 }
1588
SetResourceSchedulerParamsManagerForTests(const ResourceSchedulerParamsManager & resource_scheduler_params_manager)1589 void ResourceScheduler::SetResourceSchedulerParamsManagerForTests(
1590 const ResourceSchedulerParamsManager& resource_scheduler_params_manager) {
1591 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1592 resource_scheduler_params_manager_.Reset(resource_scheduler_params_manager);
1593 for (const auto& pair : client_map_) {
1594 pair.second->UpdateParamsForNetworkQuality();
1595 }
1596 }
1597
DispatchLongQueuedRequestsForTesting()1598 void ResourceScheduler::DispatchLongQueuedRequestsForTesting() {
1599 long_queued_requests_dispatch_timer_.Stop();
1600 OnLongQueuedRequestsDispatchTimerFired();
1601 }
1602
1603 } // namespace network
1604