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> |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
44 }; | 44 }; |
45 | 45 |
46 // Flags identifying various attributes of the request that are used | 46 // Flags identifying various attributes of the request that are used |
47 // when making scheduling decisions. | 47 // when making scheduling decisions. |
48 using RequestAttributes = uint8_t; | 48 using RequestAttributes = uint8_t; |
49 const RequestAttributes kAttributeNone = 0x00; | 49 const RequestAttributes kAttributeNone = 0x00; |
50 const RequestAttributes kAttributeInFlight = 0x01; | 50 const RequestAttributes kAttributeInFlight = 0x01; |
51 const RequestAttributes kAttributeDelayable = 0x02; | 51 const RequestAttributes kAttributeDelayable = 0x02; |
52 const RequestAttributes kAttributeLayoutBlocking = 0x04; | 52 const RequestAttributes kAttributeLayoutBlocking = 0x04; |
53 | 53 |
54 // Reasons why pending requests may be started. For logging only. | |
55 enum class RequestStartTrigger { | |
56 NONE, | |
57 COMPLETION_PRE_BODY, | |
58 COMPLETION_POST_BODY, | |
59 BODY_REACHED, | |
60 CLIENT_KILL, | |
61 SPDY_PROXY_DETECTED, | |
62 REQUEST_REPRIORITIZED, | |
63 }; | |
64 | |
65 const char* RequestStartTriggerString(RequestStartTrigger trigger) { | |
66 switch (trigger) { | |
67 case RequestStartTrigger::NONE: | |
68 return "None"; | |
69 case RequestStartTrigger::COMPLETION_PRE_BODY: | |
70 return "Request completed (pre-body tag)"; | |
71 case RequestStartTrigger::COMPLETION_POST_BODY: | |
72 return "Request completed (post-body tag)"; | |
73 case RequestStartTrigger::BODY_REACHED: | |
74 return "Body tag reached"; | |
75 case RequestStartTrigger::CLIENT_KILL: | |
76 return "Client killed"; | |
77 case RequestStartTrigger::SPDY_PROXY_DETECTED: | |
78 return "HTTPS proxy detected"; | |
mmenke
2016/12/16 16:46:46
Note that we support non-SPDY HTTPS proxies, too.
Randy Smith (Not in Mondays)
2016/12/20 16:54:17
I think this means you think I should change the s
mmenke
2017/01/03 20:32:29
Yea, I was thinking SPDY proxy detected (Or H2).
Randy Smith (Not in Mondays)
2017/01/03 22:19:29
Arggh, I was typing "HTTPS" and meaning "H2". Got
| |
79 case RequestStartTrigger::REQUEST_REPRIORITIZED: | |
80 return "Request reprioritized"; | |
mmenke
2016/12/16 16:46:46
Optional nit: I suggest just matching the enum st
Randy Smith (Not in Mondays)
2016/12/20 16:54:17
Done.
| |
81 } | |
82 NOTREACHED(); | |
83 return "Unknown"; | |
84 } | |
85 | |
54 } // namespace | 86 } // namespace |
55 | 87 |
56 // The maximum number of delayable requests to allow to be in-flight at any | 88 // The maximum number of delayable requests to allow to be in-flight at any |
57 // point in time (across all hosts). | 89 // point in time (across all hosts). |
58 static const size_t kMaxNumDelayableRequestsPerClient = 10; | 90 static const size_t kMaxNumDelayableRequestsPerClient = 10; |
59 | 91 |
60 // The maximum number of requests to allow be in-flight at any point in time per | 92 // The maximum number of requests to allow be in-flight at any point in time per |
61 // host. | 93 // host. |
62 static const size_t kMaxNumDelayableRequestsPerHostPerClient = 6; | 94 static const size_t kMaxNumDelayableRequestsPerHostPerClient = 6; |
63 | 95 |
64 // The maximum number of delayable requests to allow to be in-flight at any | 96 // The maximum number of delayable requests to allow to be in-flight at any |
65 // point in time while in the layout-blocking phase of loading. | 97 // point in time while in the layout-blocking phase of loading. |
66 static const size_t kMaxNumDelayableWhileLayoutBlockingPerClient = 1; | 98 static const size_t kMaxNumDelayableWhileLayoutBlockingPerClient = 1; |
67 | 99 |
68 // The priority level above which resources are considered layout-blocking if | 100 // The priority level above which resources are considered layout-blocking if |
69 // the html_body has not started. | 101 // the html_body has not started. |
70 static const net::RequestPriority | 102 static const net::RequestPriority |
71 kLayoutBlockingPriorityThreshold = net::MEDIUM; | 103 kLayoutBlockingPriorityThreshold = net::MEDIUM; |
72 | 104 |
73 // The priority level below which resources are considered to be delayable. | 105 // The priority level below which resources are considered to be delayable. |
74 static const net::RequestPriority | 106 static const net::RequestPriority |
75 kDelayablePriorityThreshold = net::MEDIUM; | 107 kDelayablePriorityThreshold = net::MEDIUM; |
76 | 108 |
77 // The number of in-flight layout-blocking requests above which all delayable | 109 // The number of in-flight layout-blocking requests above which all delayable |
78 // requests should be blocked. | 110 // requests should be blocked. |
79 static const size_t kInFlightNonDelayableRequestCountPerClientThreshold = 1; | 111 static const size_t kInFlightNonDelayableRequestCountPerClientThreshold = 1; |
80 | 112 |
113 // Sets an integer on construction and resets it to its original | |
114 // value on destruction. Value pointed to must be guaranteed by the caller | |
115 // to remain alive for the lifetime of this class. | |
116 template <class T> | |
117 class ScopedSetter { | |
118 public: | |
119 ScopedSetter(T* target, T value) { | |
120 target_ = target; | |
121 original_value_ = *target; | |
122 *target = value; | |
123 } | |
124 | |
125 ~ScopedSetter() { *target_ = original_value_; } | |
126 | |
127 private: | |
128 DISALLOW_COPY_AND_ASSIGN(ScopedSetter); | |
129 | |
130 T* target_; | |
131 T original_value_; | |
132 }; | |
133 | |
134 using ScopedTrigger = ScopedSetter<RequestStartTrigger>; | |
mmenke
2016/12/16 16:46:46
This seems like over-engineering to me, and I'm no
Randy Smith (Not in Mondays)
2016/12/20 16:54:17
I dunno if this helps, but I finally tracked down
mmenke
2017/01/03 20:32:29
I don't think functions should know or care exactl
Charlie Harrison
2017/01/03 21:22:49
mmenke, I'm guessing you would prefer threading th
mmenke
2017/01/03 21:24:44
Yes, that's what I'd do (Though, like I said, cert
Randy Smith (Not in Mondays)
2017/01/03 22:19:29
I hear and sympathize with you both, but I think t
| |
135 | |
81 struct ResourceScheduler::RequestPriorityParams { | 136 struct ResourceScheduler::RequestPriorityParams { |
82 RequestPriorityParams() | 137 RequestPriorityParams() |
83 : priority(net::DEFAULT_PRIORITY), | 138 : priority(net::DEFAULT_PRIORITY), |
84 intra_priority(0) { | 139 intra_priority(0) { |
85 } | 140 } |
86 | 141 |
87 RequestPriorityParams(net::RequestPriority priority, int intra_priority) | 142 RequestPriorityParams(net::RequestPriority priority, int intra_priority) |
88 : priority(priority), | 143 : priority(priority), |
89 intra_priority(intra_priority) { | 144 intra_priority(intra_priority) { |
90 } | 145 } |
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
314 | 369 |
315 // Each client represents a tab. | 370 // Each client represents a tab. |
316 class ResourceScheduler::Client { | 371 class ResourceScheduler::Client { |
317 public: | 372 public: |
318 explicit Client(bool priority_requests_delayable) | 373 explicit Client(bool priority_requests_delayable) |
319 : is_loaded_(false), | 374 : is_loaded_(false), |
320 has_html_body_(false), | 375 has_html_body_(false), |
321 using_spdy_proxy_(false), | 376 using_spdy_proxy_(false), |
322 in_flight_delayable_count_(0), | 377 in_flight_delayable_count_(0), |
323 total_layout_blocking_count_(0), | 378 total_layout_blocking_count_(0), |
324 priority_requests_delayable_(priority_requests_delayable) {} | 379 priority_requests_delayable_(priority_requests_delayable), |
380 request_start_trigger_(RequestStartTrigger::NONE) {} | |
325 | 381 |
326 ~Client() {} | 382 ~Client() {} |
327 | 383 |
328 void ScheduleRequest(net::URLRequest* url_request, | 384 void ScheduleRequest(net::URLRequest* url_request, |
329 ScheduledResourceRequest* request) { | 385 ScheduledResourceRequest* request) { |
330 SetRequestAttributes(request, DetermineRequestAttributes(request)); | 386 SetRequestAttributes(request, DetermineRequestAttributes(request)); |
331 if (ShouldStartRequest(request) == START_REQUEST) { | 387 if (ShouldStartRequest(request) == START_REQUEST) { |
332 // New requests can be started synchronously without issue. | 388 // New requests can be started synchronously without issue. |
333 StartRequest(request, START_SYNC); | 389 StartRequest(request, START_SYNC); |
334 } else { | 390 } else { |
391 request->url_request()->net_log().AddEvent( | |
392 net::NetLogEventType::RESOURCE_SCHEDULER_REQUEST_BLOCKED); | |
335 pending_requests_.Insert(request); | 393 pending_requests_.Insert(request); |
336 } | 394 } |
337 } | 395 } |
338 | 396 |
339 void RemoveRequest(ScheduledResourceRequest* request) { | 397 void RemoveRequest(ScheduledResourceRequest* request) { |
398 ScopedTrigger scoped_trigger( | |
399 &request_start_trigger_, | |
400 has_html_body_ ? RequestStartTrigger::COMPLETION_POST_BODY | |
401 : RequestStartTrigger::COMPLETION_PRE_BODY); | |
402 | |
340 if (pending_requests_.IsQueued(request)) { | 403 if (pending_requests_.IsQueued(request)) { |
341 pending_requests_.Erase(request); | 404 pending_requests_.Erase(request); |
342 DCHECK(!base::ContainsKey(in_flight_requests_, request)); | 405 DCHECK(!base::ContainsKey(in_flight_requests_, request)); |
343 } else { | 406 } else { |
344 EraseInFlightRequest(request); | 407 EraseInFlightRequest(request); |
345 | 408 |
346 // Removing this request may have freed up another to load. | 409 // Removing this request may have freed up another to load. |
347 LoadAnyStartablePendingRequests(); | 410 LoadAnyStartablePendingRequests(); |
348 } | 411 } |
349 } | 412 } |
350 | 413 |
351 RequestSet StartAndRemoveAllRequests() { | 414 RequestSet StartAndRemoveAllRequests() { |
415 ScopedTrigger scoped_trigger(&request_start_trigger_, | |
416 RequestStartTrigger::CLIENT_KILL); | |
417 | |
352 // First start any pending requests so that they will be moved into | 418 // First start any pending requests so that they will be moved into |
353 // in_flight_requests_. This may exceed the limits | 419 // in_flight_requests_. This may exceed the limits |
354 // kDefaultMaxNumDelayableRequestsPerClient and | 420 // kDefaultMaxNumDelayableRequestsPerClient and |
355 // kMaxNumDelayableRequestsPerHostPerClient, so this method must not do | 421 // kMaxNumDelayableRequestsPerHostPerClient, so this method must not do |
356 // anything that depends on those limits before calling | 422 // anything that depends on those limits before calling |
357 // ClearInFlightRequests() below. | 423 // ClearInFlightRequests() below. |
358 while (!pending_requests_.IsEmpty()) { | 424 while (!pending_requests_.IsEmpty()) { |
359 ScheduledResourceRequest* request = | 425 ScheduledResourceRequest* request = |
360 *pending_requests_.GetNextHighestIterator(); | 426 *pending_requests_.GetNextHighestIterator(); |
361 pending_requests_.Erase(request); | 427 pending_requests_.Erase(request); |
(...skipping 16 matching lines...) Expand all Loading... | |
378 void OnLoadingStateChanged(bool is_loaded) { | 444 void OnLoadingStateChanged(bool is_loaded) { |
379 is_loaded_ = is_loaded; | 445 is_loaded_ = is_loaded; |
380 } | 446 } |
381 | 447 |
382 void OnNavigate() { | 448 void OnNavigate() { |
383 has_html_body_ = false; | 449 has_html_body_ = false; |
384 is_loaded_ = false; | 450 is_loaded_ = false; |
385 } | 451 } |
386 | 452 |
387 void OnWillInsertBody() { | 453 void OnWillInsertBody() { |
454 ScopedTrigger scoped_trigger(&request_start_trigger_, | |
455 RequestStartTrigger::BODY_REACHED); | |
456 | |
388 has_html_body_ = true; | 457 has_html_body_ = true; |
389 LoadAnyStartablePendingRequests(); | 458 LoadAnyStartablePendingRequests(); |
390 } | 459 } |
391 | 460 |
392 void OnReceivedSpdyProxiedHttpResponse() { | 461 void OnReceivedSpdyProxiedHttpResponse() { |
462 ScopedTrigger scoped_trigger(&request_start_trigger_, | |
463 RequestStartTrigger::SPDY_PROXY_DETECTED); | |
464 | |
393 if (!using_spdy_proxy_) { | 465 if (!using_spdy_proxy_) { |
394 using_spdy_proxy_ = true; | 466 using_spdy_proxy_ = true; |
395 LoadAnyStartablePendingRequests(); | 467 LoadAnyStartablePendingRequests(); |
396 } | 468 } |
397 } | 469 } |
398 | 470 |
399 void ReprioritizeRequest(ScheduledResourceRequest* request, | 471 void ReprioritizeRequest(ScheduledResourceRequest* request, |
400 RequestPriorityParams old_priority_params, | 472 RequestPriorityParams old_priority_params, |
401 RequestPriorityParams new_priority_params) { | 473 RequestPriorityParams new_priority_params) { |
474 ScopedTrigger scoped_trigger(&request_start_trigger_, | |
475 RequestStartTrigger::REQUEST_REPRIORITIZED); | |
476 | |
402 request->url_request()->SetPriority(new_priority_params.priority); | 477 request->url_request()->SetPriority(new_priority_params.priority); |
403 request->set_request_priority_params(new_priority_params); | 478 request->set_request_priority_params(new_priority_params); |
404 SetRequestAttributes(request, DetermineRequestAttributes(request)); | 479 SetRequestAttributes(request, DetermineRequestAttributes(request)); |
405 if (!pending_requests_.IsQueued(request)) { | 480 if (!pending_requests_.IsQueued(request)) { |
406 DCHECK(base::ContainsKey(in_flight_requests_, request)); | 481 DCHECK(base::ContainsKey(in_flight_requests_, request)); |
407 // Request has already started. | 482 // Request has already started. |
408 return; | 483 return; |
409 } | 484 } |
410 | 485 |
411 pending_requests_.Erase(request); | 486 pending_requests_.Erase(request); |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
553 same_host_count++; | 628 same_host_count++; |
554 if (same_host_count >= kMaxNumDelayableRequestsPerHostPerClient) | 629 if (same_host_count >= kMaxNumDelayableRequestsPerHostPerClient) |
555 return true; | 630 return true; |
556 } | 631 } |
557 } | 632 } |
558 return false; | 633 return false; |
559 } | 634 } |
560 | 635 |
561 void StartRequest(ScheduledResourceRequest* request, | 636 void StartRequest(ScheduledResourceRequest* request, |
562 StartMode start_mode) { | 637 StartMode start_mode) { |
638 // Only log on requests that were blocked by the ResourceScheduler. | |
639 if (start_mode == START_ASYNC) { | |
640 request->url_request()->net_log().AddEvent( | |
641 net::NetLogEventType::RESOURCE_SCHEDULER_REQUEST_STARTED, | |
642 net::NetLog::StringCallback( | |
643 "trigger", RequestStartTriggerString(request_start_trigger_))); | |
644 } | |
563 InsertInFlightRequest(request); | 645 InsertInFlightRequest(request); |
564 request->Start(start_mode); | 646 request->Start(start_mode); |
565 } | 647 } |
566 | 648 |
567 // ShouldStartRequest is the main scheduling algorithm. | 649 // ShouldStartRequest is the main scheduling algorithm. |
568 // | 650 // |
569 // Requests are evaluated on five attributes: | 651 // Requests are evaluated on five attributes: |
570 // | 652 // |
571 // 1. Non-delayable requests: | 653 // 1. Non-delayable requests: |
572 // * Synchronous requests. | 654 // * Synchronous requests. |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
714 RequestQueue pending_requests_; | 796 RequestQueue pending_requests_; |
715 RequestSet in_flight_requests_; | 797 RequestSet in_flight_requests_; |
716 // The number of delayable in-flight requests. | 798 // The number of delayable in-flight requests. |
717 size_t in_flight_delayable_count_; | 799 size_t in_flight_delayable_count_; |
718 // The number of layout-blocking in-flight requests. | 800 // The number of layout-blocking in-flight requests. |
719 size_t total_layout_blocking_count_; | 801 size_t total_layout_blocking_count_; |
720 | 802 |
721 // True if requests to servers that support priorities (e.g., H2/QUIC) can | 803 // True if requests to servers that support priorities (e.g., H2/QUIC) can |
722 // be delayed. | 804 // be delayed. |
723 bool priority_requests_delayable_; | 805 bool priority_requests_delayable_; |
806 | |
807 // Current start trigger. | |
808 RequestStartTrigger request_start_trigger_; | |
724 }; | 809 }; |
725 | 810 |
726 ResourceScheduler::ResourceScheduler() | 811 ResourceScheduler::ResourceScheduler() |
727 : priority_requests_delayable_( | 812 : priority_requests_delayable_( |
728 base::FeatureList::IsEnabled(kPrioritySupportedRequestsDelayable)) {} | 813 base::FeatureList::IsEnabled(kPrioritySupportedRequestsDelayable)) {} |
729 | 814 |
730 ResourceScheduler::~ResourceScheduler() { | 815 ResourceScheduler::~ResourceScheduler() { |
731 DCHECK(unowned_requests_.empty()); | 816 DCHECK(unowned_requests_.empty()); |
732 DCHECK(client_map_.empty()); | 817 DCHECK(client_map_.empty()); |
733 } | 818 } |
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
914 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, | 999 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, |
915 new_priority_params); | 1000 new_priority_params); |
916 } | 1001 } |
917 | 1002 |
918 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( | 1003 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( |
919 int child_id, int route_id) { | 1004 int child_id, int route_id) { |
920 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; | 1005 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; |
921 } | 1006 } |
922 | 1007 |
923 } // namespace content | 1008 } // namespace content |
OLD | NEW |