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 |