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