| 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/macros.h" | 14 #include "base/macros.h" |
| 14 #include "base/metrics/field_trial.h" | 15 #include "base/metrics/field_trial.h" |
| 15 #include "base/metrics/histogram_macros.h" | 16 #include "base/metrics/histogram_macros.h" |
| 16 #include "base/stl_util.h" | 17 #include "base/stl_util.h" |
| 17 #include "base/supports_user_data.h" | 18 #include "base/supports_user_data.h" |
| 18 #include "content/common/resource_messages.h" | 19 #include "content/common/resource_messages.h" |
| 19 #include "content/public/browser/resource_controller.h" | 20 #include "content/public/browser/resource_controller.h" |
| 20 #include "content/public/browser/resource_request_info.h" | 21 #include "content/public/browser/resource_request_info.h" |
| 21 #include "content/public/browser/resource_throttle.h" | 22 #include "content/public/browser/resource_throttle.h" |
| 22 #include "net/base/host_port_pair.h" | 23 #include "net/base/host_port_pair.h" |
| 23 #include "net/base/load_flags.h" | 24 #include "net/base/load_flags.h" |
| 24 #include "net/base/request_priority.h" | 25 #include "net/base/request_priority.h" |
| 25 #include "net/http/http_server_properties.h" | 26 #include "net/http/http_server_properties.h" |
| 26 #include "net/url_request/url_request.h" | 27 #include "net/url_request/url_request.h" |
| 27 #include "net/url_request/url_request_context.h" | 28 #include "net/url_request/url_request_context.h" |
| 28 #include "url/scheme_host_port.h" | 29 #include "url/scheme_host_port.h" |
| 29 | 30 |
| 30 namespace content { | 31 namespace content { |
| 31 | 32 |
| 32 namespace { | 33 namespace { |
| 33 | 34 |
| 35 // When kPrioritySupportedRequestsDelayable is enabled, requests for |
| 36 // 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 // impact, see https://crbug.com/655585. |
| 39 const base::Feature kPrioritySupportedRequestsDelayable{ |
| 40 "PrioritySupportedRequestsDelayable", base::FEATURE_ENABLED_BY_DEFAULT}; |
| 41 |
| 34 enum StartMode { | 42 enum StartMode { |
| 35 START_SYNC, | 43 START_SYNC, |
| 36 START_ASYNC | 44 START_ASYNC |
| 37 }; | 45 }; |
| 38 | 46 |
| 39 // Flags identifying various attributes of the request that are used | 47 // Flags identifying various attributes of the request that are used |
| 40 // when making scheduling decisions. | 48 // when making scheduling decisions. |
| 41 using RequestAttributes = uint8_t; | 49 using RequestAttributes = uint8_t; |
| 42 const RequestAttributes kAttributeNone = 0x00; | 50 const RequestAttributes kAttributeNone = 0x00; |
| 43 const RequestAttributes kAttributeInFlight = 0x01; | 51 const RequestAttributes kAttributeInFlight = 0x01; |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 297 void ResourceScheduler::RequestQueue::Insert( | 305 void ResourceScheduler::RequestQueue::Insert( |
| 298 ScheduledResourceRequest* request) { | 306 ScheduledResourceRequest* request) { |
| 299 DCHECK(!base::ContainsKey(pointers_, request)); | 307 DCHECK(!base::ContainsKey(pointers_, request)); |
| 300 request->set_fifo_ordering(MakeFifoOrderingId()); | 308 request->set_fifo_ordering(MakeFifoOrderingId()); |
| 301 pointers_[request] = queue_.insert(request); | 309 pointers_[request] = queue_.insert(request); |
| 302 } | 310 } |
| 303 | 311 |
| 304 // Each client represents a tab. | 312 // Each client represents a tab. |
| 305 class ResourceScheduler::Client { | 313 class ResourceScheduler::Client { |
| 306 public: | 314 public: |
| 307 explicit Client(ResourceScheduler* scheduler) | 315 explicit Client(bool priority_requests_delayable) |
| 308 : is_loaded_(false), | 316 : is_loaded_(false), |
| 309 has_html_body_(false), | 317 has_html_body_(false), |
| 310 using_spdy_proxy_(false), | 318 using_spdy_proxy_(false), |
| 311 in_flight_delayable_count_(0), | 319 in_flight_delayable_count_(0), |
| 312 total_layout_blocking_count_(0) {} | 320 total_layout_blocking_count_(0), |
| 321 priority_requests_delayable_(priority_requests_delayable) {} |
| 313 | 322 |
| 314 ~Client() {} | 323 ~Client() {} |
| 315 | 324 |
| 316 void ScheduleRequest(net::URLRequest* url_request, | 325 void ScheduleRequest(net::URLRequest* url_request, |
| 317 ScheduledResourceRequest* request) { | 326 ScheduledResourceRequest* request) { |
| 318 SetRequestAttributes(request, DetermineRequestAttributes(request)); | 327 SetRequestAttributes(request, DetermineRequestAttributes(request)); |
| 319 if (ShouldStartRequest(request) == START_REQUEST) { | 328 if (ShouldStartRequest(request) == START_REQUEST) { |
| 320 // New requests can be started synchronously without issue. | 329 // New requests can be started synchronously without issue. |
| 321 StartRequest(request, START_SYNC); | 330 StartRequest(request, START_SYNC); |
| 322 } else { | 331 } else { |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 506 // attribute across redirects. | 515 // attribute across redirects. |
| 507 attributes |= kAttributeLayoutBlocking; | 516 attributes |= kAttributeLayoutBlocking; |
| 508 } else if (!has_html_body_ && | 517 } else if (!has_html_body_ && |
| 509 request->url_request()->priority() > | 518 request->url_request()->priority() > |
| 510 kLayoutBlockingPriorityThreshold) { | 519 kLayoutBlockingPriorityThreshold) { |
| 511 // Requests that are above the non_delayable threshold before the HTML | 520 // Requests that are above the non_delayable threshold before the HTML |
| 512 // body has been parsed are inferred to be layout-blocking. | 521 // body has been parsed are inferred to be layout-blocking. |
| 513 attributes |= kAttributeLayoutBlocking; | 522 attributes |= kAttributeLayoutBlocking; |
| 514 } else if (request->url_request()->priority() < | 523 } else if (request->url_request()->priority() < |
| 515 kDelayablePriorityThreshold) { | 524 kDelayablePriorityThreshold) { |
| 516 // Resources below the delayable priority threshold that are being | 525 if (priority_requests_delayable_) { |
| 517 // requested from a server that does not support native prioritization are | 526 // Resources below the delayable priority threshold that are considered |
| 518 // considered delayable. | 527 // delayable. |
| 519 url::SchemeHostPort scheme_host_port(request->url_request()->url()); | |
| 520 net::HttpServerProperties& http_server_properties = | |
| 521 *request->url_request()->context()->http_server_properties(); | |
| 522 if (!http_server_properties.SupportsRequestPriority(scheme_host_port)) | |
| 523 attributes |= kAttributeDelayable; | 528 attributes |= kAttributeDelayable; |
| 529 } else { |
| 530 // Resources below the delayable priority threshold that are being |
| 531 // requested from a server that does not support native prioritization |
| 532 // are considered delayable. |
| 533 url::SchemeHostPort scheme_host_port(request->url_request()->url()); |
| 534 net::HttpServerProperties& http_server_properties = |
| 535 *request->url_request()->context()->http_server_properties(); |
| 536 if (!http_server_properties.SupportsRequestPriority(scheme_host_port)) |
| 537 attributes |= kAttributeDelayable; |
| 538 } |
| 524 } | 539 } |
| 525 | 540 |
| 526 return attributes; | 541 return attributes; |
| 527 } | 542 } |
| 528 | 543 |
| 529 bool ShouldKeepSearching( | 544 bool ShouldKeepSearching( |
| 530 const net::HostPortPair& active_request_host) const { | 545 const net::HostPortPair& active_request_host) const { |
| 531 size_t same_host_count = 0; | 546 size_t same_host_count = 0; |
| 532 for (RequestSet::const_iterator it = in_flight_requests_.begin(); | 547 for (RequestSet::const_iterator it = in_flight_requests_.begin(); |
| 533 it != in_flight_requests_.end(); ++it) { | 548 it != in_flight_requests_.end(); ++it) { |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 588 // Syncronous requests could block the entire render, which could impact | 603 // Syncronous requests could block the entire render, which could impact |
| 589 // user-observable Clients. | 604 // user-observable Clients. |
| 590 if (!request->is_async()) | 605 if (!request->is_async()) |
| 591 return START_REQUEST; | 606 return START_REQUEST; |
| 592 | 607 |
| 593 // TODO(simonjam): This may end up causing disk contention. We should | 608 // TODO(simonjam): This may end up causing disk contention. We should |
| 594 // experiment with throttling if that happens. | 609 // experiment with throttling if that happens. |
| 595 if (!url_request.url().SchemeIsHTTPOrHTTPS()) | 610 if (!url_request.url().SchemeIsHTTPOrHTTPS()) |
| 596 return START_REQUEST; | 611 return START_REQUEST; |
| 597 | 612 |
| 598 if (using_spdy_proxy_ && url_request.url().SchemeIs(url::kHttpScheme)) | |
| 599 return START_REQUEST; | |
| 600 | |
| 601 net::HostPortPair host_port_pair = | 613 net::HostPortPair host_port_pair = |
| 602 net::HostPortPair::FromURL(url_request.url()); | 614 net::HostPortPair::FromURL(url_request.url()); |
| 603 url::SchemeHostPort scheme_host_port(url_request.url()); | |
| 604 net::HttpServerProperties& http_server_properties = | |
| 605 *url_request.context()->http_server_properties(); | |
| 606 | 615 |
| 607 // TODO(willchan): We should really improve this algorithm as described in | 616 if (!priority_requests_delayable_) { |
| 608 // crbug.com/164101. Also, theoretically we should not count a | 617 if (using_spdy_proxy_ && url_request.url().SchemeIs(url::kHttpScheme)) |
| 609 // request-priority capable request against the delayable requests limit. | 618 return START_REQUEST; |
| 610 if (http_server_properties.SupportsRequestPriority(scheme_host_port)) | 619 |
| 611 return START_REQUEST; | 620 url::SchemeHostPort scheme_host_port(url_request.url()); |
| 621 |
| 622 net::HttpServerProperties& http_server_properties = |
| 623 *url_request.context()->http_server_properties(); |
| 624 |
| 625 // TODO(willchan): We should really improve this algorithm as described in |
| 626 // crbug.com/164101. Also, theoretically we should not count a |
| 627 // request-priority capable request against the delayable requests limit. |
| 628 if (http_server_properties.SupportsRequestPriority(scheme_host_port)) |
| 629 return START_REQUEST; |
| 630 } |
| 612 | 631 |
| 613 // Non-delayable requests. | 632 // Non-delayable requests. |
| 614 if (!RequestAttributesAreSet(request->attributes(), kAttributeDelayable)) | 633 if (!RequestAttributesAreSet(request->attributes(), kAttributeDelayable)) |
| 615 return START_REQUEST; | 634 return START_REQUEST; |
| 616 | 635 |
| 617 if (in_flight_delayable_count_ >= kMaxNumDelayableRequestsPerClient) | 636 if (in_flight_delayable_count_ >= kMaxNumDelayableRequestsPerClient) |
| 618 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | 637 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; |
| 619 | 638 |
| 620 if (ShouldKeepSearching(host_port_pair)) { | 639 if (ShouldKeepSearching(host_port_pair)) { |
| 621 // There may be other requests for other hosts that may be allowed, | 640 // There may be other requests for other hosts that may be allowed, |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 691 // Tracks if the main HTML parser has reached the body which marks the end of | 710 // Tracks if the main HTML parser has reached the body which marks the end of |
| 692 // layout-blocking resources. | 711 // layout-blocking resources. |
| 693 bool has_html_body_; | 712 bool has_html_body_; |
| 694 bool using_spdy_proxy_; | 713 bool using_spdy_proxy_; |
| 695 RequestQueue pending_requests_; | 714 RequestQueue pending_requests_; |
| 696 RequestSet in_flight_requests_; | 715 RequestSet in_flight_requests_; |
| 697 // The number of delayable in-flight requests. | 716 // The number of delayable in-flight requests. |
| 698 size_t in_flight_delayable_count_; | 717 size_t in_flight_delayable_count_; |
| 699 // The number of layout-blocking in-flight requests. | 718 // The number of layout-blocking in-flight requests. |
| 700 size_t total_layout_blocking_count_; | 719 size_t total_layout_blocking_count_; |
| 720 |
| 721 // True if requests to servers that support priorities (e.g., H2/QUIC) can |
| 722 // be delayed. |
| 723 bool priority_requests_delayable_; |
| 701 }; | 724 }; |
| 702 | 725 |
| 703 ResourceScheduler::ResourceScheduler() {} | 726 ResourceScheduler::ResourceScheduler() |
| 727 : priority_requests_delayable_( |
| 728 base::FeatureList::IsEnabled(kPrioritySupportedRequestsDelayable)) {} |
| 704 | 729 |
| 705 ResourceScheduler::~ResourceScheduler() { | 730 ResourceScheduler::~ResourceScheduler() { |
| 706 DCHECK(unowned_requests_.empty()); | 731 DCHECK(unowned_requests_.empty()); |
| 707 DCHECK(client_map_.empty()); | 732 DCHECK(client_map_.empty()); |
| 708 } | 733 } |
| 709 | 734 |
| 710 std::unique_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( | 735 std::unique_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( |
| 711 int child_id, | 736 int child_id, |
| 712 int route_id, | 737 int route_id, |
| 713 bool is_async, | 738 bool is_async, |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 750 Client* client = client_it->second; | 775 Client* client = client_it->second; |
| 751 client->RemoveRequest(request); | 776 client->RemoveRequest(request); |
| 752 } | 777 } |
| 753 | 778 |
| 754 void ResourceScheduler::OnClientCreated(int child_id, | 779 void ResourceScheduler::OnClientCreated(int child_id, |
| 755 int route_id) { | 780 int route_id) { |
| 756 DCHECK(CalledOnValidThread()); | 781 DCHECK(CalledOnValidThread()); |
| 757 ClientId client_id = MakeClientId(child_id, route_id); | 782 ClientId client_id = MakeClientId(child_id, route_id); |
| 758 DCHECK(!base::ContainsKey(client_map_, client_id)); | 783 DCHECK(!base::ContainsKey(client_map_, client_id)); |
| 759 | 784 |
| 760 Client* client = new Client(this); | 785 Client* client = new Client(priority_requests_delayable_); |
| 761 client_map_[client_id] = client; | 786 client_map_[client_id] = client; |
| 762 } | 787 } |
| 763 | 788 |
| 764 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) { | 789 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) { |
| 765 DCHECK(CalledOnValidThread()); | 790 DCHECK(CalledOnValidThread()); |
| 766 ClientId client_id = MakeClientId(child_id, route_id); | 791 ClientId client_id = MakeClientId(child_id, route_id); |
| 767 ClientMap::iterator it = client_map_.find(client_id); | 792 ClientMap::iterator it = client_map_.find(client_id); |
| 768 DCHECK(it != client_map_.end()); | 793 DCHECK(it != client_map_.end()); |
| 769 | 794 |
| 770 Client* client = it->second; | 795 Client* client = it->second; |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 889 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, | 914 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, |
| 890 new_priority_params); | 915 new_priority_params); |
| 891 } | 916 } |
| 892 | 917 |
| 893 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( | 918 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( |
| 894 int child_id, int route_id) { | 919 int child_id, int route_id) { |
| 895 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; | 920 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; |
| 896 } | 921 } |
| 897 | 922 |
| 898 } // namespace content | 923 } // namespace content |
| OLD | NEW |