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

Side by Side Diff: content/browser/loader/resource_scheduler.cc

Issue 2682163002: [ResourceScheduler] Yield after starting several requests (Closed)
Patch Set: Test nits Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/loader/resource_scheduler.h" 5 #include "content/browser/loader/resource_scheduler.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 #include <set> 8 #include <set>
9 #include <string> 9 #include <string>
10 #include <utility> 10 #include <utility>
11 #include <vector> 11 #include <vector>
12 12
13 #include "base/feature_list.h" 13 #include "base/feature_list.h"
14 #include "base/macros.h" 14 #include "base/macros.h"
15 #include "base/metrics/field_trial.h" 15 #include "base/metrics/field_trial.h"
16 #include "base/metrics/field_trial_params.h"
16 #include "base/metrics/histogram_macros.h" 17 #include "base/metrics/histogram_macros.h"
17 #include "base/stl_util.h" 18 #include "base/stl_util.h"
18 #include "base/supports_user_data.h" 19 #include "base/supports_user_data.h"
19 #include "base/threading/thread_task_runner_handle.h" 20 #include "base/threading/thread_task_runner_handle.h"
20 #include "content/common/resource_messages.h" 21 #include "content/common/resource_messages.h"
21 #include "content/public/browser/resource_request_info.h" 22 #include "content/public/browser/resource_request_info.h"
22 #include "content/public/browser/resource_throttle.h" 23 #include "content/public/browser/resource_throttle.h"
23 #include "net/base/host_port_pair.h" 24 #include "net/base/host_port_pair.h"
24 #include "net/base/load_flags.h" 25 #include "net/base/load_flags.h"
25 #include "net/base/request_priority.h" 26 #include "net/base/request_priority.h"
26 #include "net/http/http_server_properties.h" 27 #include "net/http/http_server_properties.h"
27 #include "net/url_request/url_request.h" 28 #include "net/url_request/url_request.h"
28 #include "net/url_request/url_request_context.h" 29 #include "net/url_request/url_request_context.h"
29 #include "url/scheme_host_port.h" 30 #include "url/scheme_host_port.h"
30 31
31 namespace content { 32 namespace content {
32 33
33 namespace { 34 namespace {
34 35
35 // When kPrioritySupportedRequestsDelayable is enabled, requests for 36 // When kPrioritySupportedRequestsDelayable is enabled, requests for
36 // H2/QUIC/SPDY resources can be delayed by the ResourceScheduler just as 37 // H2/QUIC/SPDY resources can be delayed by the ResourceScheduler just as
37 // HTTP/1.1 resources are. Disabling this appears to have negative performance 38 // HTTP/1.1 resources are. Disabling this appears to have negative performance
38 // impact, see https://crbug.com/655585. 39 // impact, see https://crbug.com/655585.
39 const base::Feature kPrioritySupportedRequestsDelayable{ 40 const base::Feature kPrioritySupportedRequestsDelayable{
40 "PrioritySupportedRequestsDelayable", base::FEATURE_DISABLED_BY_DEFAULT}; 41 "PrioritySupportedRequestsDelayable", base::FEATURE_DISABLED_BY_DEFAULT};
41 42
43 // In the event that many resource requests are started quickly, this feature
44 // will periodically yield (e.g., delaying starting of requests) by posting a
45 // task and waiting for the task to run to resume. This allows other
46 // operations that rely on the IO thread (e.g., already running network
47 // requests) to make progress.
48 const base::Feature kNetworkSchedulerYielding{
49 "NetworkSchedulerYielding", base::FEATURE_DISABLED_BY_DEFAULT};
50 const char kMaxRequestsBeforeYieldingParam[] = "MaxRequestsBeforeYieldingParam";
51 const int kMaxRequestsBeforeYieldingDefault = 5;
52
42 enum StartMode { 53 enum StartMode {
43 START_SYNC, 54 START_SYNC,
44 START_ASYNC 55 START_ASYNC
45 }; 56 };
46 57
47 // Flags identifying various attributes of the request that are used 58 // Flags identifying various attributes of the request that are used
48 // when making scheduling decisions. 59 // when making scheduling decisions.
49 using RequestAttributes = uint8_t; 60 using RequestAttributes = uint8_t;
50 const RequestAttributes kAttributeNone = 0x00; 61 const RequestAttributes kAttributeNone = 0x00;
51 const RequestAttributes kAttributeInFlight = 0x01; 62 const RequestAttributes kAttributeInFlight = 0x01;
52 const RequestAttributes kAttributeDelayable = 0x02; 63 const RequestAttributes kAttributeDelayable = 0x02;
53 const RequestAttributes kAttributeLayoutBlocking = 0x04; 64 const RequestAttributes kAttributeLayoutBlocking = 0x04;
54 65
55 // Reasons why pending requests may be started. For logging only. 66 // Reasons why pending requests may be started. For logging only.
56 enum class RequestStartTrigger { 67 enum class RequestStartTrigger {
57 NONE, 68 NONE,
58 COMPLETION_PRE_BODY, 69 COMPLETION_PRE_BODY,
59 COMPLETION_POST_BODY, 70 COMPLETION_POST_BODY,
60 BODY_REACHED, 71 BODY_REACHED,
61 CLIENT_KILL, 72 CLIENT_KILL,
62 SPDY_PROXY_DETECTED, 73 SPDY_PROXY_DETECTED,
63 REQUEST_REPRIORITIZED, 74 REQUEST_REPRIORITIZED,
75 START_YIELDED,
Charlie Harrison 2017/02/09 19:08:04 nit: I think START_WAS_YIELDED is a bit better.
jkarlin 2017/02/09 19:48:12 Done.
64 }; 76 };
65 77
66 const char* RequestStartTriggerString(RequestStartTrigger trigger) { 78 const char* RequestStartTriggerString(RequestStartTrigger trigger) {
67 switch (trigger) { 79 switch (trigger) {
68 case RequestStartTrigger::NONE: 80 case RequestStartTrigger::NONE:
69 return "NONE"; 81 return "NONE";
70 case RequestStartTrigger::COMPLETION_PRE_BODY: 82 case RequestStartTrigger::COMPLETION_PRE_BODY:
71 return "COMPLETION_PRE_BODY"; 83 return "COMPLETION_PRE_BODY";
72 case RequestStartTrigger::COMPLETION_POST_BODY: 84 case RequestStartTrigger::COMPLETION_POST_BODY:
73 return "COMPLETION_POST_BODY"; 85 return "COMPLETION_POST_BODY";
74 case RequestStartTrigger::BODY_REACHED: 86 case RequestStartTrigger::BODY_REACHED:
75 return "BODY_REACHED"; 87 return "BODY_REACHED";
76 case RequestStartTrigger::CLIENT_KILL: 88 case RequestStartTrigger::CLIENT_KILL:
77 return "CLIENT_KILL"; 89 return "CLIENT_KILL";
78 case RequestStartTrigger::SPDY_PROXY_DETECTED: 90 case RequestStartTrigger::SPDY_PROXY_DETECTED:
79 return "SPDY_PROXY_DETECTED"; 91 return "SPDY_PROXY_DETECTED";
80 case RequestStartTrigger::REQUEST_REPRIORITIZED: 92 case RequestStartTrigger::REQUEST_REPRIORITIZED:
81 return "REQUEST_REPRIORITIZED"; 93 return "REQUEST_REPRIORITIZED";
94 case RequestStartTrigger::START_YIELDED:
95 return "START_YIELDED";
82 } 96 }
83 NOTREACHED(); 97 NOTREACHED();
84 return "Unknown"; 98 return "Unknown";
85 } 99 }
86 100
87 } // namespace 101 } // namespace
88 102
89 // The maximum number of delayable requests to allow to be in-flight at any 103 // The maximum number of delayable requests to allow to be in-flight at any
90 // point in time (across all hosts). 104 // point in time (across all hosts).
91 static const size_t kMaxNumDelayableRequestsPerClient = 10; 105 static const size_t kMaxNumDelayableRequestsPerClient = 10;
(...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after
341 void ResourceScheduler::RequestQueue::Insert( 355 void ResourceScheduler::RequestQueue::Insert(
342 ScheduledResourceRequest* request) { 356 ScheduledResourceRequest* request) {
343 DCHECK(!base::ContainsKey(pointers_, request)); 357 DCHECK(!base::ContainsKey(pointers_, request));
344 request->set_fifo_ordering(MakeFifoOrderingId()); 358 request->set_fifo_ordering(MakeFifoOrderingId());
345 pointers_[request] = queue_.insert(request); 359 pointers_[request] = queue_.insert(request);
346 } 360 }
347 361
348 // Each client represents a tab. 362 // Each client represents a tab.
349 class ResourceScheduler::Client { 363 class ResourceScheduler::Client {
350 public: 364 public:
351 explicit Client(bool priority_requests_delayable) 365 Client(bool priority_requests_delayable,
366 bool yielding_scheduler,
367 int max_requests_before_yielding)
352 : is_loaded_(false), 368 : is_loaded_(false),
353 has_html_body_(false), 369 has_html_body_(false),
354 using_spdy_proxy_(false), 370 using_spdy_proxy_(false),
355 in_flight_delayable_count_(0), 371 in_flight_delayable_count_(0),
356 total_layout_blocking_count_(0), 372 total_layout_blocking_count_(0),
357 priority_requests_delayable_(priority_requests_delayable), 373 priority_requests_delayable_(priority_requests_delayable),
358 has_pending_start_task_(false), 374 has_pending_start_task_(false),
375 requests_since_yielding_(0),
376 yielding_scheduler_(yielding_scheduler),
377 max_requests_before_yielding_(max_requests_before_yielding),
359 weak_ptr_factory_(this) {} 378 weak_ptr_factory_(this) {}
360 379
361 ~Client() {} 380 ~Client() {}
362 381
363 void ScheduleRequest(net::URLRequest* url_request, 382 void ScheduleRequest(net::URLRequest* url_request,
364 ScheduledResourceRequest* request) { 383 ScheduledResourceRequest* request) {
365 SetRequestAttributes(request, DetermineRequestAttributes(request)); 384 SetRequestAttributes(request, DetermineRequestAttributes(request));
366 if (ShouldStartRequest(request) == START_REQUEST) { 385 if (ShouldStartRequest(request) == START_REQUEST &&
Charlie Harrison 2017/02/09 19:08:04 Eek, I am a little worried about sync XHRs here. M
jkarlin 2017/02/09 19:48:13 Nice catch. I've changed it to not yield for sync
386 !ShouldYieldScheduler()) {
367 // New requests can be started synchronously without issue. 387 // New requests can be started synchronously without issue.
368 StartRequest(request, START_SYNC, RequestStartTrigger::NONE); 388 StartRequest(request, START_SYNC, RequestStartTrigger::NONE);
369 } else { 389 } else {
370 pending_requests_.Insert(request); 390 pending_requests_.Insert(request);
371 } 391 }
372 } 392 }
373 393
374 void RemoveRequest(ScheduledResourceRequest* request) { 394 void RemoveRequest(ScheduledResourceRequest* request) {
375 if (pending_requests_.IsQueued(request)) { 395 if (pending_requests_.IsQueued(request)) {
376 pending_requests_.Erase(request); 396 pending_requests_.Erase(request);
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after
731 // pattern. 751 // pattern.
732 void ScheduleLoadAnyStartablePendingRequests(RequestStartTrigger trigger) { 752 void ScheduleLoadAnyStartablePendingRequests(RequestStartTrigger trigger) {
733 if (has_pending_start_task_) 753 if (has_pending_start_task_)
734 return; 754 return;
735 has_pending_start_task_ = true; 755 has_pending_start_task_ = true;
736 base::ThreadTaskRunnerHandle::Get()->PostTask( 756 base::ThreadTaskRunnerHandle::Get()->PostTask(
737 FROM_HERE, base::Bind(&Client::LoadAnyStartablePendingRequests, 757 FROM_HERE, base::Bind(&Client::LoadAnyStartablePendingRequests,
738 weak_ptr_factory_.GetWeakPtr(), trigger)); 758 weak_ptr_factory_.GetWeakPtr(), trigger));
739 } 759 }
740 760
761 void ResumeAfterYielding() {
762 requests_since_yielding_ = 0;
Charlie Harrison 2017/02/09 19:08:04 // A single request can trigger this method, so do
jkarlin 2017/02/09 19:48:13 Thanks, I forgot to add that back when I switched
763 LoadAnyStartablePendingRequests(RequestStartTrigger::START_YIELDED);
764 }
765
766 // Returns true if the scheduler should not start the next request and
767 // instead return so that other tasks can run on the IO thread (e.g.,
768 // existing network requests). If it returns true, ShouldYieldScheduler will
769 // have already scheduled a task to resume after yielding.
770 bool ShouldYieldScheduler() {
771 if (!yielding_scheduler_)
772 return false;
773
774 if (requests_since_yielding_ == 0) {
Charlie Harrison 2017/02/09 19:08:04 The fact that we post a task immediately deserves
jkarlin 2017/02/09 19:48:12 Done.
775 base::ThreadTaskRunnerHandle::Get()->PostTask(
776 FROM_HERE, base::Bind(&Client::ResumeAfterYielding,
777 weak_ptr_factory_.GetWeakPtr()));
778 }
779
780 return requests_since_yielding_++ >= max_requests_before_yielding_;
Charlie Harrison 2017/02/09 19:08:04 optional: Can you put the increment on a separate
jkarlin 2017/02/09 19:48:12 Done.
781 }
782
741 void LoadAnyStartablePendingRequests(RequestStartTrigger trigger) { 783 void LoadAnyStartablePendingRequests(RequestStartTrigger trigger) {
742 // We iterate through all the pending requests, starting with the highest 784 // We iterate through all the pending requests, starting with the highest
743 // priority one. For each entry, one of three things can happen: 785 // priority one. For each entry, one of three things can happen:
744 // 1) We start the request, remove it from the list, and keep checking. 786 // 1) We start the request, remove it from the list, and keep checking.
745 // 2) We do NOT start the request, but ShouldStartRequest() signals us that 787 // 2) We do NOT start the request, but ShouldStartRequest() signals us that
746 // there may be room for other requests, so we keep checking and leave 788 // there may be room for other requests, so we keep checking and leave
747 // the previous request still in the list. 789 // the previous request still in the list.
748 // 3) We do not start the request, same as above, but StartRequest() tells 790 // 3) We do not start the request, same as above, but StartRequest() tells
749 // us there's no point in checking any further requests. 791 // us there's no point in checking any further requests.
750 has_pending_start_task_ = false; 792 has_pending_start_task_ = false;
751 RequestQueue::NetQueue::iterator request_iter = 793 RequestQueue::NetQueue::iterator request_iter =
752 pending_requests_.GetNextHighestIterator(); 794 pending_requests_.GetNextHighestIterator();
753 795
754 while (request_iter != pending_requests_.End()) { 796 while (request_iter != pending_requests_.End()) {
755 ScheduledResourceRequest* request = *request_iter; 797 ScheduledResourceRequest* request = *request_iter;
756 ShouldStartReqResult query_result = ShouldStartRequest(request); 798 ShouldStartReqResult query_result = ShouldStartRequest(request);
757 799
758 if (query_result == START_REQUEST) { 800 if (query_result == START_REQUEST) {
801 if (ShouldYieldScheduler())
802 break;
803
759 pending_requests_.Erase(request); 804 pending_requests_.Erase(request);
760 StartRequest(request, START_ASYNC, trigger); 805 StartRequest(request, START_ASYNC, trigger);
761 806
762 // StartRequest can modify the pending list, so we (re)start evaluation 807 // StartRequest can modify the pending list, so we (re)start evaluation
763 // from the currently highest priority request. Avoid copying a singular 808 // from the currently highest priority request. Avoid copying a singular
764 // iterator, which would trigger undefined behavior. 809 // iterator, which would trigger undefined behavior.
765 if (pending_requests_.GetNextHighestIterator() == 810 if (pending_requests_.GetNextHighestIterator() ==
766 pending_requests_.End()) 811 pending_requests_.End())
767 break; 812 break;
768 request_iter = pending_requests_.GetNextHighestIterator(); 813 request_iter = pending_requests_.GetNextHighestIterator();
(...skipping 18 matching lines...) Expand all
787 size_t in_flight_delayable_count_; 832 size_t in_flight_delayable_count_;
788 // The number of layout-blocking in-flight requests. 833 // The number of layout-blocking in-flight requests.
789 size_t total_layout_blocking_count_; 834 size_t total_layout_blocking_count_;
790 835
791 // True if requests to servers that support priorities (e.g., H2/QUIC) can 836 // True if requests to servers that support priorities (e.g., H2/QUIC) can
792 // be delayed. 837 // be delayed.
793 bool priority_requests_delayable_; 838 bool priority_requests_delayable_;
794 839
795 bool has_pending_start_task_; 840 bool has_pending_start_task_;
796 841
842 // The number of requests that have been started since the last
843 // ResumeAfterYielding task was posted.
844 int requests_since_yielding_;
845
846 // Whether or not to periodically yield when starting lots of requests.
847 bool yielding_scheduler_;
848
849 // The number of requests that can start before yielding.
850 int max_requests_before_yielding_;
851
797 base::WeakPtrFactory<ResourceScheduler::Client> weak_ptr_factory_; 852 base::WeakPtrFactory<ResourceScheduler::Client> weak_ptr_factory_;
798 }; 853 };
799 854
800 ResourceScheduler::ResourceScheduler() 855 ResourceScheduler::ResourceScheduler()
801 : priority_requests_delayable_( 856 : priority_requests_delayable_(
802 base::FeatureList::IsEnabled(kPrioritySupportedRequestsDelayable)) {} 857 base::FeatureList::IsEnabled(kPrioritySupportedRequestsDelayable)),
858 yielding_scheduler_(
859 base::FeatureList::IsEnabled(kNetworkSchedulerYielding)),
860 max_requests_before_yielding_(base::GetFieldTrialParamByFeatureAsInt(
861 kNetworkSchedulerYielding,
862 kMaxRequestsBeforeYieldingParam,
863 kMaxRequestsBeforeYieldingDefault)) {}
803 864
804 ResourceScheduler::~ResourceScheduler() { 865 ResourceScheduler::~ResourceScheduler() {
805 DCHECK(unowned_requests_.empty()); 866 DCHECK(unowned_requests_.empty());
806 DCHECK(client_map_.empty()); 867 DCHECK(client_map_.empty());
807 } 868 }
808 869
809 std::unique_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( 870 std::unique_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest(
810 int child_id, 871 int child_id,
811 int route_id, 872 int route_id,
812 bool is_async, 873 bool is_async,
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
849 Client* client = client_it->second; 910 Client* client = client_it->second;
850 client->RemoveRequest(request); 911 client->RemoveRequest(request);
851 } 912 }
852 913
853 void ResourceScheduler::OnClientCreated(int child_id, 914 void ResourceScheduler::OnClientCreated(int child_id,
854 int route_id) { 915 int route_id) {
855 DCHECK(CalledOnValidThread()); 916 DCHECK(CalledOnValidThread());
856 ClientId client_id = MakeClientId(child_id, route_id); 917 ClientId client_id = MakeClientId(child_id, route_id);
857 DCHECK(!base::ContainsKey(client_map_, client_id)); 918 DCHECK(!base::ContainsKey(client_map_, client_id));
858 919
859 Client* client = new Client(priority_requests_delayable_); 920 Client* client = new Client(priority_requests_delayable_, yielding_scheduler_,
921 max_requests_before_yielding_);
860 client_map_[client_id] = client; 922 client_map_[client_id] = client;
861 } 923 }
862 924
863 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) { 925 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) {
864 DCHECK(CalledOnValidThread()); 926 DCHECK(CalledOnValidThread());
865 ClientId client_id = MakeClientId(child_id, route_id); 927 ClientId client_id = MakeClientId(child_id, route_id);
866 ClientMap::iterator it = client_map_.find(client_id); 928 ClientMap::iterator it = client_map_.find(client_id);
867 DCHECK(it != client_map_.end()); 929 DCHECK(it != client_map_.end());
868 930
869 Client* client = it->second; 931 Client* client = it->second;
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
988 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, 1050 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params,
989 new_priority_params); 1051 new_priority_params);
990 } 1052 }
991 1053
992 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( 1054 ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
993 int child_id, int route_id) { 1055 int child_id, int route_id) {
994 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; 1056 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id;
995 } 1057 }
996 1058
997 } // namespace content 1059 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698