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

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

Issue 2682163002: [ResourceScheduler] Yield after starting several requests (Closed)
Patch Set: Address comments from PS7 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_WAS_YIELDED,
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_WAS_YIELDED:
95 return "START_WAS_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_enabled,
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_enabled_(yielding_scheduler_enabled),
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 &&
386 (!request->is_async() || !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 bool yieldedRequests =
Charlie Harrison 2017/02/09 20:43:29 nit: yielded_requests. Too much blink style for yo
jkarlin 2017/02/09 20:51:21 Done.
763 requests_since_yielding_ > max_requests_before_yielding_;
764 requests_since_yielding_ = 0;
765
766 if (yieldedRequests)
767 LoadAnyStartablePendingRequests(RequestStartTrigger::START_WAS_YIELDED);
768 }
769
770 // Returns true if the scheduler should not start the next request and
771 // instead return so that other tasks can run on the IO thread (e.g.,
772 // existing network requests). If it returns true, ShouldYieldScheduler will
773 // have already scheduled a task to resume after yielding.
774 bool ShouldYieldScheduler() {
775 if (!yielding_scheduler_enabled_)
776 return false;
777
778 requests_since_yielding_ += 1;
779
780 if (requests_since_yielding_ == 1) {
781 // This is the first request since last yielding. Post a task to reset the
782 // counter and start any yielded tasks if necessary. We post this now
783 // instead of when we first yield so that if there is a pause between
784 // requests the counter is reset.
785 base::ThreadTaskRunnerHandle::Get()->PostTask(
786 FROM_HERE, base::Bind(&Client::ResumeAfterYielding,
787 weak_ptr_factory_.GetWeakPtr()));
788 }
789
790 return requests_since_yielding_ > max_requests_before_yielding_;
791 }
792
741 void LoadAnyStartablePendingRequests(RequestStartTrigger trigger) { 793 void LoadAnyStartablePendingRequests(RequestStartTrigger trigger) {
742 // We iterate through all the pending requests, starting with the highest 794 // We iterate through all the pending requests, starting with the highest
743 // priority one. For each entry, one of three things can happen: 795 // 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. 796 // 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 797 // 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 798 // there may be room for other requests, so we keep checking and leave
747 // the previous request still in the list. 799 // the previous request still in the list.
748 // 3) We do not start the request, same as above, but StartRequest() tells 800 // 3) We do not start the request, same as above, but StartRequest() tells
749 // us there's no point in checking any further requests. 801 // us there's no point in checking any further requests.
750 has_pending_start_task_ = false; 802 has_pending_start_task_ = false;
751 RequestQueue::NetQueue::iterator request_iter = 803 RequestQueue::NetQueue::iterator request_iter =
752 pending_requests_.GetNextHighestIterator(); 804 pending_requests_.GetNextHighestIterator();
753 805
754 while (request_iter != pending_requests_.End()) { 806 while (request_iter != pending_requests_.End()) {
755 ScheduledResourceRequest* request = *request_iter; 807 ScheduledResourceRequest* request = *request_iter;
756 ShouldStartReqResult query_result = ShouldStartRequest(request); 808 ShouldStartReqResult query_result = ShouldStartRequest(request);
757 809
758 if (query_result == START_REQUEST) { 810 if (query_result == START_REQUEST) {
811 if (ShouldYieldScheduler())
812 break;
813
759 pending_requests_.Erase(request); 814 pending_requests_.Erase(request);
760 StartRequest(request, START_ASYNC, trigger); 815 StartRequest(request, START_ASYNC, trigger);
761 816
762 // StartRequest can modify the pending list, so we (re)start evaluation 817 // StartRequest can modify the pending list, so we (re)start evaluation
763 // from the currently highest priority request. Avoid copying a singular 818 // from the currently highest priority request. Avoid copying a singular
764 // iterator, which would trigger undefined behavior. 819 // iterator, which would trigger undefined behavior.
765 if (pending_requests_.GetNextHighestIterator() == 820 if (pending_requests_.GetNextHighestIterator() ==
766 pending_requests_.End()) 821 pending_requests_.End())
767 break; 822 break;
768 request_iter = pending_requests_.GetNextHighestIterator(); 823 request_iter = pending_requests_.GetNextHighestIterator();
(...skipping 18 matching lines...) Expand all
787 size_t in_flight_delayable_count_; 842 size_t in_flight_delayable_count_;
788 // The number of layout-blocking in-flight requests. 843 // The number of layout-blocking in-flight requests.
789 size_t total_layout_blocking_count_; 844 size_t total_layout_blocking_count_;
790 845
791 // True if requests to servers that support priorities (e.g., H2/QUIC) can 846 // True if requests to servers that support priorities (e.g., H2/QUIC) can
792 // be delayed. 847 // be delayed.
793 bool priority_requests_delayable_; 848 bool priority_requests_delayable_;
794 849
795 bool has_pending_start_task_; 850 bool has_pending_start_task_;
796 851
852 // The number of requests that have been started since the last
853 // ResumeAfterYielding task was posted.
854 int requests_since_yielding_;
855
856 // Whether or not to periodically yield when starting lots of requests.
857 bool yielding_scheduler_enabled_;
858
859 // The number of requests that can start before yielding.
860 int max_requests_before_yielding_;
861
797 base::WeakPtrFactory<ResourceScheduler::Client> weak_ptr_factory_; 862 base::WeakPtrFactory<ResourceScheduler::Client> weak_ptr_factory_;
798 }; 863 };
799 864
800 ResourceScheduler::ResourceScheduler() 865 ResourceScheduler::ResourceScheduler()
801 : priority_requests_delayable_( 866 : priority_requests_delayable_(
802 base::FeatureList::IsEnabled(kPrioritySupportedRequestsDelayable)) {} 867 base::FeatureList::IsEnabled(kPrioritySupportedRequestsDelayable)),
868 yielding_scheduler_enabled_(
869 base::FeatureList::IsEnabled(kNetworkSchedulerYielding)),
870 max_requests_before_yielding_(base::GetFieldTrialParamByFeatureAsInt(
871 kNetworkSchedulerYielding,
872 kMaxRequestsBeforeYieldingParam,
873 kMaxRequestsBeforeYieldingDefault)) {}
803 874
804 ResourceScheduler::~ResourceScheduler() { 875 ResourceScheduler::~ResourceScheduler() {
805 DCHECK(unowned_requests_.empty()); 876 DCHECK(unowned_requests_.empty());
806 DCHECK(client_map_.empty()); 877 DCHECK(client_map_.empty());
807 } 878 }
808 879
809 std::unique_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( 880 std::unique_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest(
810 int child_id, 881 int child_id,
811 int route_id, 882 int route_id,
812 bool is_async, 883 bool is_async,
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
849 Client* client = client_it->second; 920 Client* client = client_it->second;
850 client->RemoveRequest(request); 921 client->RemoveRequest(request);
851 } 922 }
852 923
853 void ResourceScheduler::OnClientCreated(int child_id, 924 void ResourceScheduler::OnClientCreated(int child_id,
854 int route_id) { 925 int route_id) {
855 DCHECK(CalledOnValidThread()); 926 DCHECK(CalledOnValidThread());
856 ClientId client_id = MakeClientId(child_id, route_id); 927 ClientId client_id = MakeClientId(child_id, route_id);
857 DCHECK(!base::ContainsKey(client_map_, client_id)); 928 DCHECK(!base::ContainsKey(client_map_, client_id));
858 929
859 Client* client = new Client(priority_requests_delayable_); 930 Client* client =
931 new Client(priority_requests_delayable_, yielding_scheduler_enabled_,
932 max_requests_before_yielding_);
860 client_map_[client_id] = client; 933 client_map_[client_id] = client;
861 } 934 }
862 935
863 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) { 936 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) {
864 DCHECK(CalledOnValidThread()); 937 DCHECK(CalledOnValidThread());
865 ClientId client_id = MakeClientId(child_id, route_id); 938 ClientId client_id = MakeClientId(child_id, route_id);
866 ClientMap::iterator it = client_map_.find(client_id); 939 ClientMap::iterator it = client_map_.find(client_id);
867 DCHECK(it != client_map_.end()); 940 DCHECK(it != client_map_.end());
868 941
869 Client* client = it->second; 942 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, 1061 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params,
989 new_priority_params); 1062 new_priority_params);
990 } 1063 }
991 1064
992 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( 1065 ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
993 int child_id, int route_id) { 1066 int child_id, int route_id) {
994 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; 1067 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id;
995 } 1068 }
996 1069
997 } // namespace content 1070 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/loader/resource_scheduler.h ('k') | content/browser/loader/resource_scheduler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698