| OLD | NEW |
| 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/field_trial_params.h" |
| 17 #include "base/metrics/histogram_macros.h" | 17 #include "base/metrics/histogram_macros.h" |
| 18 #include "base/stl_util.h" | 18 #include "base/stl_util.h" |
| 19 #include "base/supports_user_data.h" | 19 #include "base/supports_user_data.h" |
| 20 #include "base/threading/thread_task_runner_handle.h" | 20 #include "base/threading/thread_task_runner_handle.h" |
| 21 #include "base/trace_event/trace_event.h" |
| 21 #include "content/common/resource_messages.h" | 22 #include "content/common/resource_messages.h" |
| 22 #include "content/public/browser/resource_request_info.h" | 23 #include "content/public/browser/resource_request_info.h" |
| 23 #include "content/public/browser/resource_throttle.h" | 24 #include "content/public/browser/resource_throttle.h" |
| 24 #include "net/base/host_port_pair.h" | 25 #include "net/base/host_port_pair.h" |
| 25 #include "net/base/load_flags.h" | 26 #include "net/base/load_flags.h" |
| 26 #include "net/base/request_priority.h" | 27 #include "net/base/request_priority.h" |
| 27 #include "net/http/http_server_properties.h" | 28 #include "net/http/http_server_properties.h" |
| 28 #include "net/url_request/url_request.h" | 29 #include "net/url_request/url_request.h" |
| 29 #include "net/url_request/url_request_context.h" | 30 #include "net/url_request/url_request_context.h" |
| 30 #include "url/scheme_host_port.h" | 31 #include "url/scheme_host_port.h" |
| (...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 364 public: | 365 public: |
| 365 Client(bool priority_requests_delayable, | 366 Client(bool priority_requests_delayable, |
| 366 bool yielding_scheduler_enabled, | 367 bool yielding_scheduler_enabled, |
| 367 int max_requests_before_yielding) | 368 int max_requests_before_yielding) |
| 368 : is_loaded_(false), | 369 : is_loaded_(false), |
| 369 has_html_body_(false), | 370 has_html_body_(false), |
| 370 using_spdy_proxy_(false), | 371 using_spdy_proxy_(false), |
| 371 in_flight_delayable_count_(0), | 372 in_flight_delayable_count_(0), |
| 372 total_layout_blocking_count_(0), | 373 total_layout_blocking_count_(0), |
| 373 priority_requests_delayable_(priority_requests_delayable), | 374 priority_requests_delayable_(priority_requests_delayable), |
| 374 has_pending_start_task_(false), | 375 num_skipped_scans_due_to_scheduled_start_(0), |
| 375 started_requests_since_yielding_(0), | 376 started_requests_since_yielding_(0), |
| 376 did_scheduler_yield_(false), | 377 did_scheduler_yield_(false), |
| 377 yielding_scheduler_enabled_(yielding_scheduler_enabled), | 378 yielding_scheduler_enabled_(yielding_scheduler_enabled), |
| 378 max_requests_before_yielding_(max_requests_before_yielding), | 379 max_requests_before_yielding_(max_requests_before_yielding), |
| 379 weak_ptr_factory_(this) {} | 380 weak_ptr_factory_(this) {} |
| 380 | 381 |
| 381 ~Client() {} | 382 ~Client() {} |
| 382 | 383 |
| 383 void ScheduleRequest(net::URLRequest* url_request, | 384 void ScheduleRequest(net::URLRequest* url_request, |
| 384 ScheduledResourceRequest* request) { | 385 ScheduledResourceRequest* request) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 395 } | 396 } |
| 396 | 397 |
| 397 void RemoveRequest(ScheduledResourceRequest* request) { | 398 void RemoveRequest(ScheduledResourceRequest* request) { |
| 398 if (pending_requests_.IsQueued(request)) { | 399 if (pending_requests_.IsQueued(request)) { |
| 399 pending_requests_.Erase(request); | 400 pending_requests_.Erase(request); |
| 400 DCHECK(!base::ContainsKey(in_flight_requests_, request)); | 401 DCHECK(!base::ContainsKey(in_flight_requests_, request)); |
| 401 } else { | 402 } else { |
| 402 EraseInFlightRequest(request); | 403 EraseInFlightRequest(request); |
| 403 | 404 |
| 404 // Removing this request may have freed up another to load. | 405 // Removing this request may have freed up another to load. |
| 405 ScheduleLoadAnyStartablePendingRequests( | 406 LoadAnyStartablePendingRequests( |
| 406 has_html_body_ ? RequestStartTrigger::COMPLETION_POST_BODY | 407 has_html_body_ ? RequestStartTrigger::COMPLETION_POST_BODY |
| 407 : RequestStartTrigger::COMPLETION_PRE_BODY); | 408 : RequestStartTrigger::COMPLETION_PRE_BODY); |
| 408 } | 409 } |
| 409 } | 410 } |
| 410 | 411 |
| 411 RequestSet StartAndRemoveAllRequests() { | 412 RequestSet StartAndRemoveAllRequests() { |
| 412 // First start any pending requests so that they will be moved into | 413 // First start any pending requests so that they will be moved into |
| 413 // in_flight_requests_. This may exceed the limits | 414 // in_flight_requests_. This may exceed the limits |
| 414 // kDefaultMaxNumDelayableRequestsPerClient and | 415 // kDefaultMaxNumDelayableRequestsPerClient and |
| 415 // kMaxNumDelayableRequestsPerHostPerClient, so this method must not do | 416 // kMaxNumDelayableRequestsPerHostPerClient, so this method must not do |
| (...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 758 // It is common for a burst of messages to come from the renderer which | 759 // It is common for a burst of messages to come from the renderer which |
| 759 // trigger starting pending requests. Naively, this would result in O(n*m) | 760 // trigger starting pending requests. Naively, this would result in O(n*m) |
| 760 // behavior for n pending requests and m <= n messages, as | 761 // behavior for n pending requests and m <= n messages, as |
| 761 // LoadAnyStartablePendingRequest is O(n) for n pending requests. To solve | 762 // LoadAnyStartablePendingRequest is O(n) for n pending requests. To solve |
| 762 // this, just post a task to the end of the queue to call the method, | 763 // this, just post a task to the end of the queue to call the method, |
| 763 // coalescing the m messages into a single call to | 764 // coalescing the m messages into a single call to |
| 764 // LoadAnyStartablePendingRequests. | 765 // LoadAnyStartablePendingRequests. |
| 765 // TODO(csharrison): Reconsider this if IPC batching becomes an easy to use | 766 // TODO(csharrison): Reconsider this if IPC batching becomes an easy to use |
| 766 // pattern. | 767 // pattern. |
| 767 void ScheduleLoadAnyStartablePendingRequests(RequestStartTrigger trigger) { | 768 void ScheduleLoadAnyStartablePendingRequests(RequestStartTrigger trigger) { |
| 768 if (has_pending_start_task_) | 769 if (num_skipped_scans_due_to_scheduled_start_ == 0) { |
| 769 return; | 770 TRACE_EVENT0("loading", "ScheduleLoadAnyStartablePendingRequests"); |
| 770 has_pending_start_task_ = true; | 771 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 771 base::ThreadTaskRunnerHandle::Get()->PostTask( | 772 FROM_HERE, |
| 772 FROM_HERE, base::Bind(&Client::LoadAnyStartablePendingRequests, | 773 base::Bind(&Client::LoadAnyStartablePendingRequests, |
| 773 weak_ptr_factory_.GetWeakPtr(), trigger)); | 774 weak_ptr_factory_.GetWeakPtr(), trigger)); |
| 775 } |
| 776 num_skipped_scans_due_to_scheduled_start_ += 1; |
| 774 } | 777 } |
| 775 | 778 |
| 776 void ResumeIfYielded() { | 779 void ResumeIfYielded() { |
| 777 bool yielded = did_scheduler_yield_; | 780 bool yielded = did_scheduler_yield_; |
| 778 started_requests_since_yielding_ = 0; | 781 started_requests_since_yielding_ = 0; |
| 779 did_scheduler_yield_ = false; | 782 did_scheduler_yield_ = false; |
| 780 | 783 |
| 781 if (yielded) | 784 if (yielded) |
| 782 LoadAnyStartablePendingRequests(RequestStartTrigger::START_WAS_YIELDED); | 785 LoadAnyStartablePendingRequests(RequestStartTrigger::START_WAS_YIELDED); |
| 783 } | 786 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 796 | 799 |
| 797 void LoadAnyStartablePendingRequests(RequestStartTrigger trigger) { | 800 void LoadAnyStartablePendingRequests(RequestStartTrigger trigger) { |
| 798 // We iterate through all the pending requests, starting with the highest | 801 // We iterate through all the pending requests, starting with the highest |
| 799 // priority one. For each entry, one of three things can happen: | 802 // priority one. For each entry, one of three things can happen: |
| 800 // 1) We start the request, remove it from the list, and keep checking. | 803 // 1) We start the request, remove it from the list, and keep checking. |
| 801 // 2) We do NOT start the request, but ShouldStartRequest() signals us that | 804 // 2) We do NOT start the request, but ShouldStartRequest() signals us that |
| 802 // there may be room for other requests, so we keep checking and leave | 805 // there may be room for other requests, so we keep checking and leave |
| 803 // the previous request still in the list. | 806 // the previous request still in the list. |
| 804 // 3) We do not start the request, same as above, but StartRequest() tells | 807 // 3) We do not start the request, same as above, but StartRequest() tells |
| 805 // us there's no point in checking any further requests. | 808 // us there's no point in checking any further requests. |
| 806 has_pending_start_task_ = false; | 809 TRACE_EVENT0("loading", "LoadAnyStartablePendingRequests"); |
| 810 if (num_skipped_scans_due_to_scheduled_start_ > 0) { |
| 811 UMA_HISTOGRAM_COUNTS_1M("ResourceScheduler.NumSkippedScans.ScheduleStart", |
| 812 num_skipped_scans_due_to_scheduled_start_); |
| 813 } |
| 814 num_skipped_scans_due_to_scheduled_start_ = 0; |
| 807 RequestQueue::NetQueue::iterator request_iter = | 815 RequestQueue::NetQueue::iterator request_iter = |
| 808 pending_requests_.GetNextHighestIterator(); | 816 pending_requests_.GetNextHighestIterator(); |
| 809 | 817 |
| 810 while (request_iter != pending_requests_.End()) { | 818 while (request_iter != pending_requests_.End()) { |
| 811 ScheduledResourceRequest* request = *request_iter; | 819 ScheduledResourceRequest* request = *request_iter; |
| 812 ShouldStartReqResult query_result = ShouldStartRequest(request); | 820 ShouldStartReqResult query_result = ShouldStartRequest(request); |
| 813 | 821 |
| 814 if (query_result == START_REQUEST) { | 822 if (query_result == START_REQUEST) { |
| 815 pending_requests_.Erase(request); | 823 pending_requests_.Erase(request); |
| 816 StartRequest(request, START_ASYNC, trigger); | 824 StartRequest(request, START_ASYNC, trigger); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 844 RequestSet in_flight_requests_; | 852 RequestSet in_flight_requests_; |
| 845 // The number of delayable in-flight requests. | 853 // The number of delayable in-flight requests. |
| 846 size_t in_flight_delayable_count_; | 854 size_t in_flight_delayable_count_; |
| 847 // The number of layout-blocking in-flight requests. | 855 // The number of layout-blocking in-flight requests. |
| 848 size_t total_layout_blocking_count_; | 856 size_t total_layout_blocking_count_; |
| 849 | 857 |
| 850 // True if requests to servers that support priorities (e.g., H2/QUIC) can | 858 // True if requests to servers that support priorities (e.g., H2/QUIC) can |
| 851 // be delayed. | 859 // be delayed. |
| 852 bool priority_requests_delayable_; | 860 bool priority_requests_delayable_; |
| 853 | 861 |
| 854 bool has_pending_start_task_; | 862 // The number of LoadAnyStartablePendingRequests scans that were skipped due |
| 863 // to smarter task scheduling around reprioritization. |
| 864 int num_skipped_scans_due_to_scheduled_start_; |
| 855 | 865 |
| 856 // The number of started requests since the last ResumeIfYielded task was | 866 // The number of started requests since the last ResumeIfYielded task was |
| 857 // run. | 867 // run. |
| 858 int started_requests_since_yielding_; | 868 int started_requests_since_yielding_; |
| 859 | 869 |
| 860 // If the scheduler had to yield the start of a request since the last | 870 // If the scheduler had to yield the start of a request since the last |
| 861 // ResumeIfYielded task was run. | 871 // ResumeIfYielded task was run. |
| 862 bool did_scheduler_yield_; | 872 bool did_scheduler_yield_; |
| 863 | 873 |
| 864 // Whether or not to periodically yield when starting lots of requests. | 874 // Whether or not to periodically yield when starting lots of requests. |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1069 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, | 1079 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, |
| 1070 new_priority_params); | 1080 new_priority_params); |
| 1071 } | 1081 } |
| 1072 | 1082 |
| 1073 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( | 1083 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( |
| 1074 int child_id, int route_id) { | 1084 int child_id, int route_id) { |
| 1075 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; | 1085 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; |
| 1076 } | 1086 } |
| 1077 | 1087 |
| 1078 } // namespace content | 1088 } // namespace content |
| OLD | NEW |