| 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/macros.h" | 13 #include "base/macros.h" |
| 14 #include "base/metrics/field_trial.h" | 14 #include "base/metrics/field_trial.h" |
| 15 #include "base/metrics/histogram_macros.h" | 15 #include "base/metrics/histogram_macros.h" |
| 16 #include "base/stl_util.h" | 16 #include "base/stl_util.h" |
| 17 #include "base/strings/string_number_conversions.h" | |
| 18 #include "base/strings/string_piece.h" | |
| 19 #include "base/supports_user_data.h" | 17 #include "base/supports_user_data.h" |
| 20 #include "content/common/resource_messages.h" | 18 #include "content/common/resource_messages.h" |
| 21 #include "content/public/browser/resource_controller.h" | 19 #include "content/public/browser/resource_controller.h" |
| 22 #include "content/public/browser/resource_request_info.h" | 20 #include "content/public/browser/resource_request_info.h" |
| 23 #include "content/public/browser/resource_throttle.h" | 21 #include "content/public/browser/resource_throttle.h" |
| 24 #include "net/base/host_port_pair.h" | 22 #include "net/base/host_port_pair.h" |
| 25 #include "net/base/load_flags.h" | 23 #include "net/base/load_flags.h" |
| 26 #include "net/base/request_priority.h" | 24 #include "net/base/request_priority.h" |
| 27 #include "net/http/http_server_properties.h" | 25 #include "net/http/http_server_properties.h" |
| 28 #include "net/url_request/url_request.h" | 26 #include "net/url_request/url_request.h" |
| 29 #include "net/url_request/url_request_context.h" | 27 #include "net/url_request/url_request_context.h" |
| 30 #include "url/scheme_host_port.h" | 28 #include "url/scheme_host_port.h" |
| 31 | 29 |
| 32 namespace content { | 30 namespace content { |
| 33 | 31 |
| 34 namespace { | 32 namespace { |
| 35 | 33 |
| 36 enum StartMode { | 34 enum StartMode { |
| 37 START_SYNC, | 35 START_SYNC, |
| 38 START_ASYNC | 36 START_ASYNC |
| 39 }; | 37 }; |
| 40 | 38 |
| 41 // Field trial constants | |
| 42 const char kRequestLimitFieldTrial[] = "OutstandingRequestLimiting"; | |
| 43 const char kRequestLimitFieldTrialGroupPrefix[] = "Limit"; | |
| 44 | |
| 45 // Flags identifying various attributes of the request that are used | 39 // Flags identifying various attributes of the request that are used |
| 46 // when making scheduling decisions. | 40 // when making scheduling decisions. |
| 47 using RequestAttributes = uint8_t; | 41 using RequestAttributes = uint8_t; |
| 48 const RequestAttributes kAttributeNone = 0x00; | 42 const RequestAttributes kAttributeNone = 0x00; |
| 49 const RequestAttributes kAttributeInFlight = 0x01; | 43 const RequestAttributes kAttributeInFlight = 0x01; |
| 50 const RequestAttributes kAttributeDelayable = 0x02; | 44 const RequestAttributes kAttributeDelayable = 0x02; |
| 51 const RequestAttributes kAttributeLayoutBlocking = 0x04; | 45 const RequestAttributes kAttributeLayoutBlocking = 0x04; |
| 52 | 46 |
| 53 } // namespace | 47 } // namespace |
| 54 | 48 |
| (...skipping 252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 307 pointers_[request] = queue_.insert(request); | 301 pointers_[request] = queue_.insert(request); |
| 308 } | 302 } |
| 309 | 303 |
| 310 // Each client represents a tab. | 304 // Each client represents a tab. |
| 311 class ResourceScheduler::Client { | 305 class ResourceScheduler::Client { |
| 312 public: | 306 public: |
| 313 explicit Client(ResourceScheduler* scheduler) | 307 explicit Client(ResourceScheduler* scheduler) |
| 314 : is_loaded_(false), | 308 : is_loaded_(false), |
| 315 has_html_body_(false), | 309 has_html_body_(false), |
| 316 using_spdy_proxy_(false), | 310 using_spdy_proxy_(false), |
| 317 scheduler_(scheduler), | |
| 318 in_flight_delayable_count_(0), | 311 in_flight_delayable_count_(0), |
| 319 total_layout_blocking_count_(0) {} | 312 total_layout_blocking_count_(0) {} |
| 320 | 313 |
| 321 ~Client() {} | 314 ~Client() {} |
| 322 | 315 |
| 323 void ScheduleRequest(net::URLRequest* url_request, | 316 void ScheduleRequest(net::URLRequest* url_request, |
| 324 ScheduledResourceRequest* request) { | 317 ScheduledResourceRequest* request) { |
| 325 SetRequestAttributes(request, DetermineRequestAttributes(request)); | 318 SetRequestAttributes(request, DetermineRequestAttributes(request)); |
| 326 if (ShouldStartRequest(request) == START_REQUEST) { | 319 if (ShouldStartRequest(request) == START_REQUEST) { |
| 327 // New requests can be started synchronously without issue. | 320 // New requests can be started synchronously without issue. |
| (...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 570 // | 563 // |
| 571 // 4. Layout-blocking requests: | 564 // 4. Layout-blocking requests: |
| 572 // * High-priority requests (> net::LOW) initiated before the renderer has | 565 // * High-priority requests (> net::LOW) initiated before the renderer has |
| 573 // a <body>. | 566 // a <body>. |
| 574 // | 567 // |
| 575 // 5. Low priority requests | 568 // 5. Low priority requests |
| 576 // | 569 // |
| 577 // The following rules are followed: | 570 // The following rules are followed: |
| 578 // | 571 // |
| 579 // All types of requests: | 572 // All types of requests: |
| 580 // * If an outstanding request limit is in place, only that number | |
| 581 // of requests may be in flight for a single client at the same time. | |
| 582 // * Non-delayable, High-priority and request-priority capable requests are | 573 // * Non-delayable, High-priority and request-priority capable requests are |
| 583 // issued immediately. | 574 // issued immediately. |
| 584 // * Low priority requests are delayable. | 575 // * Low priority requests are delayable. |
| 585 // * While kInFlightNonDelayableRequestCountPerClientThreshold | 576 // * While kInFlightNonDelayableRequestCountPerClientThreshold |
| 586 // layout-blocking requests are loading or the body tag has not yet been | 577 // layout-blocking requests are loading or the body tag has not yet been |
| 587 // parsed, limit the number of delayable requests that may be in flight | 578 // parsed, limit the number of delayable requests that may be in flight |
| 588 // (to kMaxNumDelayableWhileLayoutBlockingPerClient by default, or to zero | 579 // to kMaxNumDelayableWhileLayoutBlockingPerClient. |
| 589 // if there's an outstanding request limit in place). | |
| 590 // * If no high priority or layout-blocking requests are in flight, start | 580 // * If no high priority or layout-blocking requests are in flight, start |
| 591 // loading delayable requests. | 581 // loading delayable requests. |
| 592 // * Never exceed 10 delayable requests in flight per client. | 582 // * Never exceed 10 delayable requests in flight per client. |
| 593 // * Never exceed 6 delayable requests for a given host. | 583 // * Never exceed 6 delayable requests for a given host. |
| 594 | 584 |
| 595 ShouldStartReqResult ShouldStartRequest( | 585 ShouldStartReqResult ShouldStartRequest( |
| 596 ScheduledResourceRequest* request) const { | 586 ScheduledResourceRequest* request) const { |
| 597 const net::URLRequest& url_request = *request->url_request(); | 587 const net::URLRequest& url_request = *request->url_request(); |
| 598 // Syncronous requests could block the entire render, which could impact | 588 // Syncronous requests could block the entire render, which could impact |
| 599 // user-observable Clients. | 589 // user-observable Clients. |
| 600 if (!request->is_async()) | 590 if (!request->is_async()) |
| 601 return START_REQUEST; | 591 return START_REQUEST; |
| 602 | 592 |
| 603 // TODO(simonjam): This may end up causing disk contention. We should | 593 // TODO(simonjam): This may end up causing disk contention. We should |
| 604 // experiment with throttling if that happens. | 594 // experiment with throttling if that happens. |
| 605 if (!url_request.url().SchemeIsHTTPOrHTTPS()) | 595 if (!url_request.url().SchemeIsHTTPOrHTTPS()) |
| 606 return START_REQUEST; | 596 return START_REQUEST; |
| 607 | 597 |
| 608 if (using_spdy_proxy_ && url_request.url().SchemeIs(url::kHttpScheme)) | 598 if (using_spdy_proxy_ && url_request.url().SchemeIs(url::kHttpScheme)) |
| 609 return START_REQUEST; | 599 return START_REQUEST; |
| 610 | 600 |
| 611 // Implementation of the kRequestLimitFieldTrial. | |
| 612 if (scheduler_->limit_outstanding_requests() && | |
| 613 in_flight_requests_.size() >= scheduler_->outstanding_request_limit()) { | |
| 614 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | |
| 615 } | |
| 616 | |
| 617 net::HostPortPair host_port_pair = | 601 net::HostPortPair host_port_pair = |
| 618 net::HostPortPair::FromURL(url_request.url()); | 602 net::HostPortPair::FromURL(url_request.url()); |
| 619 url::SchemeHostPort scheme_host_port(url_request.url()); | 603 url::SchemeHostPort scheme_host_port(url_request.url()); |
| 620 net::HttpServerProperties& http_server_properties = | 604 net::HttpServerProperties& http_server_properties = |
| 621 *url_request.context()->http_server_properties(); | 605 *url_request.context()->http_server_properties(); |
| 622 | 606 |
| 623 // TODO(willchan): We should really improve this algorithm as described in | 607 // TODO(willchan): We should really improve this algorithm as described in |
| 624 // crbug.com/164101. Also, theoretically we should not count a | 608 // crbug.com/164101. Also, theoretically we should not count a |
| 625 // request-priority capable request against the delayable requests limit. | 609 // request-priority capable request against the delayable requests limit. |
| 626 if (http_server_properties.SupportsRequestPriority(scheme_host_port)) | 610 if (http_server_properties.SupportsRequestPriority(scheme_host_port)) |
| (...skipping 20 matching lines...) Expand all Loading... |
| 647 if (!has_html_body_ || total_layout_blocking_count_ != 0) { | 631 if (!has_html_body_ || total_layout_blocking_count_ != 0) { |
| 648 size_t non_delayable_requests_in_flight_count = | 632 size_t non_delayable_requests_in_flight_count = |
| 649 in_flight_requests_.size() - in_flight_delayable_count_; | 633 in_flight_requests_.size() - in_flight_delayable_count_; |
| 650 if (non_delayable_requests_in_flight_count > | 634 if (non_delayable_requests_in_flight_count > |
| 651 kInFlightNonDelayableRequestCountPerClientThreshold) { | 635 kInFlightNonDelayableRequestCountPerClientThreshold) { |
| 652 // Too many higher priority in-flight requests to allow lower priority | 636 // Too many higher priority in-flight requests to allow lower priority |
| 653 // requests through. | 637 // requests through. |
| 654 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | 638 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; |
| 655 } | 639 } |
| 656 if (in_flight_requests_.size() > 0 && | 640 if (in_flight_requests_.size() > 0 && |
| 657 (scheduler_->limit_outstanding_requests() || | 641 (in_flight_delayable_count_ >= |
| 658 in_flight_delayable_count_ >= | |
| 659 kMaxNumDelayableWhileLayoutBlockingPerClient)) { | 642 kMaxNumDelayableWhileLayoutBlockingPerClient)) { |
| 660 // Block the request if at least one request is in flight and the | 643 // Block the request if at least one request is in flight and the |
| 661 // number of in-flight delayable requests has hit the configured | 644 // number of in-flight delayable requests has hit the configured |
| 662 // limit. | 645 // limit. |
| 663 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | 646 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; |
| 664 } | 647 } |
| 665 } | 648 } |
| 666 | 649 |
| 667 return START_REQUEST; | 650 return START_REQUEST; |
| 668 } | 651 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 704 } | 687 } |
| 705 } | 688 } |
| 706 | 689 |
| 707 bool is_loaded_; | 690 bool is_loaded_; |
| 708 // Tracks if the main HTML parser has reached the body which marks the end of | 691 // Tracks if the main HTML parser has reached the body which marks the end of |
| 709 // layout-blocking resources. | 692 // layout-blocking resources. |
| 710 bool has_html_body_; | 693 bool has_html_body_; |
| 711 bool using_spdy_proxy_; | 694 bool using_spdy_proxy_; |
| 712 RequestQueue pending_requests_; | 695 RequestQueue pending_requests_; |
| 713 RequestSet in_flight_requests_; | 696 RequestSet in_flight_requests_; |
| 714 ResourceScheduler* scheduler_; | |
| 715 // The number of delayable in-flight requests. | 697 // The number of delayable in-flight requests. |
| 716 size_t in_flight_delayable_count_; | 698 size_t in_flight_delayable_count_; |
| 717 // The number of layout-blocking in-flight requests. | 699 // The number of layout-blocking in-flight requests. |
| 718 size_t total_layout_blocking_count_; | 700 size_t total_layout_blocking_count_; |
| 719 }; | 701 }; |
| 720 | 702 |
| 721 ResourceScheduler::ResourceScheduler() | 703 ResourceScheduler::ResourceScheduler() {} |
| 722 : limit_outstanding_requests_(false), | |
| 723 outstanding_request_limit_(0) { | |
| 724 std::string outstanding_limit_trial_group = | |
| 725 base::FieldTrialList::FindFullName(kRequestLimitFieldTrial); | |
| 726 std::vector<std::string> split_group( | |
| 727 base::SplitString(outstanding_limit_trial_group, "=", | |
| 728 base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)); | |
| 729 int outstanding_limit = 0; | |
| 730 if (split_group.size() == 2 && | |
| 731 split_group[0] == kRequestLimitFieldTrialGroupPrefix && | |
| 732 base::StringToInt(split_group[1], &outstanding_limit) && | |
| 733 outstanding_limit > 0) { | |
| 734 limit_outstanding_requests_ = true; | |
| 735 outstanding_request_limit_ = outstanding_limit; | |
| 736 } | |
| 737 } | |
| 738 | 704 |
| 739 ResourceScheduler::~ResourceScheduler() { | 705 ResourceScheduler::~ResourceScheduler() { |
| 740 DCHECK(unowned_requests_.empty()); | 706 DCHECK(unowned_requests_.empty()); |
| 741 DCHECK(client_map_.empty()); | 707 DCHECK(client_map_.empty()); |
| 742 } | 708 } |
| 743 | 709 |
| 744 std::unique_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( | 710 std::unique_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( |
| 745 int child_id, | 711 int child_id, |
| 746 int route_id, | 712 int route_id, |
| 747 bool is_async, | 713 bool is_async, |
| (...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 923 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, | 889 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, |
| 924 new_priority_params); | 890 new_priority_params); |
| 925 } | 891 } |
| 926 | 892 |
| 927 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( | 893 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( |
| 928 int child_id, int route_id) { | 894 int child_id, int route_id) { |
| 929 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; | 895 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; |
| 930 } | 896 } |
| 931 | 897 |
| 932 } // namespace content | 898 } // namespace content |
| OLD | NEW |