Chromium Code Reviews| 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 "COMPLETION_PRE_BODY"; | |
| 71 case RequestStartTrigger::COMPLETION_POST_BODY: | |
| 72 return "COMPLETION_POST_BODY"; | |
| 73 case RequestStartTrigger::BODY_REACHED: | |
| 74 return "BODY_REACHED"; | |
| 75 case RequestStartTrigger::CLIENT_KILL: | |
| 76 return "CLIENT_KILL"; | |
| 77 case RequestStartTrigger::SPDY_PROXY_DETECTED: | |
| 78 return "SPDY_PROXY_DETECTED"; | |
| 79 case RequestStartTrigger::REQUEST_REPRIORITIZED: | |
| 80 return "REQUEST_REPRIORITIZED"; | |
| 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 |
| (...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 323 total_layout_blocking_count_(0), | 355 total_layout_blocking_count_(0), |
| 324 priority_requests_delayable_(priority_requests_delayable) {} | 356 priority_requests_delayable_(priority_requests_delayable) {} |
| 325 | 357 |
| 326 ~Client() {} | 358 ~Client() {} |
| 327 | 359 |
| 328 void ScheduleRequest(net::URLRequest* url_request, | 360 void ScheduleRequest(net::URLRequest* url_request, |
| 329 ScheduledResourceRequest* request) { | 361 ScheduledResourceRequest* request) { |
| 330 SetRequestAttributes(request, DetermineRequestAttributes(request)); | 362 SetRequestAttributes(request, DetermineRequestAttributes(request)); |
| 331 if (ShouldStartRequest(request) == START_REQUEST) { | 363 if (ShouldStartRequest(request) == START_REQUEST) { |
| 332 // New requests can be started synchronously without issue. | 364 // New requests can be started synchronously without issue. |
| 333 StartRequest(request, START_SYNC); | 365 StartRequest(request, START_SYNC, RequestStartTrigger::NONE); |
| 334 } else { | 366 } else { |
| 335 pending_requests_.Insert(request); | 367 pending_requests_.Insert(request); |
| 336 } | 368 } |
| 337 } | 369 } |
| 338 | 370 |
| 339 void RemoveRequest(ScheduledResourceRequest* request) { | 371 void RemoveRequest(ScheduledResourceRequest* request) { |
| 340 if (pending_requests_.IsQueued(request)) { | 372 if (pending_requests_.IsQueued(request)) { |
| 341 pending_requests_.Erase(request); | 373 pending_requests_.Erase(request); |
| 342 DCHECK(!base::ContainsKey(in_flight_requests_, request)); | 374 DCHECK(!base::ContainsKey(in_flight_requests_, request)); |
| 343 } else { | 375 } else { |
| 344 EraseInFlightRequest(request); | 376 EraseInFlightRequest(request); |
| 345 | 377 |
| 346 // Removing this request may have freed up another to load. | 378 // Removing this request may have freed up another to load. |
| 347 LoadAnyStartablePendingRequests(); | 379 LoadAnyStartablePendingRequests( |
| 380 has_html_body_ | |
| 381 ? RequestStartTrigger::COMPLETION_POST_BODY | |
| 382 : RequestStartTrigger::COMPLETION_PRE_BODY); | |
| 348 } | 383 } |
| 349 } | 384 } |
| 350 | 385 |
| 351 RequestSet StartAndRemoveAllRequests() { | 386 RequestSet StartAndRemoveAllRequests() { |
| 352 // First start any pending requests so that they will be moved into | 387 // First start any pending requests so that they will be moved into |
| 353 // in_flight_requests_. This may exceed the limits | 388 // in_flight_requests_. This may exceed the limits |
| 354 // kDefaultMaxNumDelayableRequestsPerClient and | 389 // kDefaultMaxNumDelayableRequestsPerClient and |
| 355 // kMaxNumDelayableRequestsPerHostPerClient, so this method must not do | 390 // kMaxNumDelayableRequestsPerHostPerClient, so this method must not do |
| 356 // anything that depends on those limits before calling | 391 // anything that depends on those limits before calling |
| 357 // ClearInFlightRequests() below. | 392 // ClearInFlightRequests() below. |
| 358 while (!pending_requests_.IsEmpty()) { | 393 while (!pending_requests_.IsEmpty()) { |
| 359 ScheduledResourceRequest* request = | 394 ScheduledResourceRequest* request = |
| 360 *pending_requests_.GetNextHighestIterator(); | 395 *pending_requests_.GetNextHighestIterator(); |
| 361 pending_requests_.Erase(request); | 396 pending_requests_.Erase(request); |
| 362 // Starting requests asynchronously ensures no side effects, and avoids | 397 // Starting requests asynchronously ensures no side effects, and avoids |
| 363 // starting a bunch of requests that may be about to be deleted. | 398 // starting a bunch of requests that may be about to be deleted. |
| 364 StartRequest(request, START_ASYNC); | 399 StartRequest(request, START_ASYNC, RequestStartTrigger::CLIENT_KILL); |
| 365 } | 400 } |
| 366 RequestSet unowned_requests; | 401 RequestSet unowned_requests; |
| 367 for (RequestSet::iterator it = in_flight_requests_.begin(); | 402 for (RequestSet::iterator it = in_flight_requests_.begin(); |
| 368 it != in_flight_requests_.end(); ++it) { | 403 it != in_flight_requests_.end(); ++it) { |
| 369 unowned_requests.insert(*it); | 404 unowned_requests.insert(*it); |
| 370 (*it)->set_attributes(kAttributeNone); | 405 (*it)->set_attributes(kAttributeNone); |
| 371 } | 406 } |
| 372 ClearInFlightRequests(); | 407 ClearInFlightRequests(); |
| 373 return unowned_requests; | 408 return unowned_requests; |
| 374 } | 409 } |
| 375 | 410 |
| 376 bool is_loaded() const { return is_loaded_; } | 411 bool is_loaded() const { return is_loaded_; } |
| 377 | 412 |
| 378 void OnLoadingStateChanged(bool is_loaded) { | 413 void OnLoadingStateChanged(bool is_loaded) { |
| 379 is_loaded_ = is_loaded; | 414 is_loaded_ = is_loaded; |
| 380 } | 415 } |
| 381 | 416 |
| 382 void OnNavigate() { | 417 void OnNavigate() { |
| 383 has_html_body_ = false; | 418 has_html_body_ = false; |
| 384 is_loaded_ = false; | 419 is_loaded_ = false; |
| 385 } | 420 } |
| 386 | 421 |
| 387 void OnWillInsertBody() { | 422 void OnWillInsertBody() { |
| 388 has_html_body_ = true; | 423 has_html_body_ = true; |
| 389 LoadAnyStartablePendingRequests(); | 424 LoadAnyStartablePendingRequests(RequestStartTrigger::BODY_REACHED); |
| 390 } | 425 } |
| 391 | 426 |
| 392 void OnReceivedSpdyProxiedHttpResponse() { | 427 void OnReceivedSpdyProxiedHttpResponse() { |
| 393 if (!using_spdy_proxy_) { | 428 if (!using_spdy_proxy_) { |
| 394 using_spdy_proxy_ = true; | 429 using_spdy_proxy_ = true; |
| 395 LoadAnyStartablePendingRequests(); | 430 LoadAnyStartablePendingRequests(RequestStartTrigger::SPDY_PROXY_DETECTED); |
| 396 } | 431 } |
| 397 } | 432 } |
| 398 | 433 |
| 399 void ReprioritizeRequest(ScheduledResourceRequest* request, | 434 void ReprioritizeRequest(ScheduledResourceRequest* request, |
| 400 RequestPriorityParams old_priority_params, | 435 RequestPriorityParams old_priority_params, |
| 401 RequestPriorityParams new_priority_params) { | 436 RequestPriorityParams new_priority_params) { |
| 402 request->url_request()->SetPriority(new_priority_params.priority); | 437 request->url_request()->SetPriority(new_priority_params.priority); |
| 403 request->set_request_priority_params(new_priority_params); | 438 request->set_request_priority_params(new_priority_params); |
| 404 SetRequestAttributes(request, DetermineRequestAttributes(request)); | 439 SetRequestAttributes(request, DetermineRequestAttributes(request)); |
| 405 if (!pending_requests_.IsQueued(request)) { | 440 if (!pending_requests_.IsQueued(request)) { |
| 406 DCHECK(base::ContainsKey(in_flight_requests_, request)); | 441 DCHECK(base::ContainsKey(in_flight_requests_, request)); |
| 407 // Request has already started. | 442 // Request has already started. |
| 408 return; | 443 return; |
| 409 } | 444 } |
| 410 | 445 |
| 411 pending_requests_.Erase(request); | 446 pending_requests_.Erase(request); |
| 412 pending_requests_.Insert(request); | 447 pending_requests_.Insert(request); |
| 413 | 448 |
| 414 if (new_priority_params.priority > old_priority_params.priority) { | 449 if (new_priority_params.priority > old_priority_params.priority) { |
| 415 // Check if this request is now able to load at its new priority. | 450 // Check if this request is now able to load at its new priority. |
| 416 LoadAnyStartablePendingRequests(); | 451 LoadAnyStartablePendingRequests( |
| 452 RequestStartTrigger::REQUEST_REPRIORITIZED); | |
| 417 } | 453 } |
| 418 } | 454 } |
| 419 | 455 |
| 420 private: | 456 private: |
| 421 enum ShouldStartReqResult { | 457 enum ShouldStartReqResult { |
| 422 DO_NOT_START_REQUEST_AND_STOP_SEARCHING, | 458 DO_NOT_START_REQUEST_AND_STOP_SEARCHING, |
| 423 DO_NOT_START_REQUEST_AND_KEEP_SEARCHING, | 459 DO_NOT_START_REQUEST_AND_KEEP_SEARCHING, |
| 424 START_REQUEST, | 460 START_REQUEST, |
| 425 }; | 461 }; |
| 426 | 462 |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 552 if (active_request_host.Equals((*it)->host_port_pair())) { | 588 if (active_request_host.Equals((*it)->host_port_pair())) { |
| 553 same_host_count++; | 589 same_host_count++; |
| 554 if (same_host_count >= kMaxNumDelayableRequestsPerHostPerClient) | 590 if (same_host_count >= kMaxNumDelayableRequestsPerHostPerClient) |
| 555 return true; | 591 return true; |
| 556 } | 592 } |
| 557 } | 593 } |
| 558 return false; | 594 return false; |
| 559 } | 595 } |
| 560 | 596 |
| 561 void StartRequest(ScheduledResourceRequest* request, | 597 void StartRequest(ScheduledResourceRequest* request, |
| 562 StartMode start_mode) { | 598 StartMode start_mode, |
| 599 RequestStartTrigger trigger) { | |
| 600 // Only log on requests that were blocked by the ResourceScheduler. | |
| 601 if (start_mode == START_ASYNC) { | |
|
mmenke
2017/01/03 22:24:18
optional: Would it be better to check if trigger
Charlie Harrison
2017/01/03 22:26:47
Whichever, DCHECKing that start_mode == START_ASYN
Randy Smith (Not in Mondays)
2017/01/03 22:37:09
I'd prefer this check, since just conditionalizing
| |
| 602 request->url_request()->net_log().AddEvent( | |
| 603 net::NetLogEventType::RESOURCE_SCHEDULER_REQUEST_STARTED, | |
| 604 net::NetLog::StringCallback( | |
| 605 "trigger", RequestStartTriggerString(trigger))); | |
| 606 } | |
| 563 InsertInFlightRequest(request); | 607 InsertInFlightRequest(request); |
| 564 request->Start(start_mode); | 608 request->Start(start_mode); |
| 565 } | 609 } |
| 566 | 610 |
| 567 // ShouldStartRequest is the main scheduling algorithm. | 611 // ShouldStartRequest is the main scheduling algorithm. |
| 568 // | 612 // |
| 569 // Requests are evaluated on five attributes: | 613 // Requests are evaluated on five attributes: |
| 570 // | 614 // |
| 571 // 1. Non-delayable requests: | 615 // 1. Non-delayable requests: |
| 572 // * Synchronous requests. | 616 // * Synchronous requests. |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 662 // Block the request if at least one request is in flight and the | 706 // Block the request if at least one request is in flight and the |
| 663 // number of in-flight delayable requests has hit the configured | 707 // number of in-flight delayable requests has hit the configured |
| 664 // limit. | 708 // limit. |
| 665 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | 709 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; |
| 666 } | 710 } |
| 667 } | 711 } |
| 668 | 712 |
| 669 return START_REQUEST; | 713 return START_REQUEST; |
| 670 } | 714 } |
| 671 | 715 |
| 672 void LoadAnyStartablePendingRequests() { | 716 void LoadAnyStartablePendingRequests(RequestStartTrigger trigger) { |
| 673 // We iterate through all the pending requests, starting with the highest | 717 // We iterate through all the pending requests, starting with the highest |
| 674 // priority one. For each entry, one of three things can happen: | 718 // priority one. For each entry, one of three things can happen: |
| 675 // 1) We start the request, remove it from the list, and keep checking. | 719 // 1) We start the request, remove it from the list, and keep checking. |
| 676 // 2) We do NOT start the request, but ShouldStartRequest() signals us that | 720 // 2) We do NOT start the request, but ShouldStartRequest() signals us that |
| 677 // there may be room for other requests, so we keep checking and leave | 721 // there may be room for other requests, so we keep checking and leave |
| 678 // the previous request still in the list. | 722 // the previous request still in the list. |
| 679 // 3) We do not start the request, same as above, but StartRequest() tells | 723 // 3) We do not start the request, same as above, but StartRequest() tells |
| 680 // us there's no point in checking any further requests. | 724 // us there's no point in checking any further requests. |
| 681 RequestQueue::NetQueue::iterator request_iter = | 725 RequestQueue::NetQueue::iterator request_iter = |
| 682 pending_requests_.GetNextHighestIterator(); | 726 pending_requests_.GetNextHighestIterator(); |
| 683 | 727 |
| 684 while (request_iter != pending_requests_.End()) { | 728 while (request_iter != pending_requests_.End()) { |
| 685 ScheduledResourceRequest* request = *request_iter; | 729 ScheduledResourceRequest* request = *request_iter; |
| 686 ShouldStartReqResult query_result = ShouldStartRequest(request); | 730 ShouldStartReqResult query_result = ShouldStartRequest(request); |
| 687 | 731 |
| 688 if (query_result == START_REQUEST) { | 732 if (query_result == START_REQUEST) { |
| 689 pending_requests_.Erase(request); | 733 pending_requests_.Erase(request); |
| 690 StartRequest(request, START_ASYNC); | 734 StartRequest(request, START_ASYNC, trigger); |
| 691 | 735 |
| 692 // StartRequest can modify the pending list, so we (re)start evaluation | 736 // StartRequest can modify the pending list, so we (re)start evaluation |
| 693 // from the currently highest priority request. Avoid copying a singular | 737 // from the currently highest priority request. Avoid copying a singular |
| 694 // iterator, which would trigger undefined behavior. | 738 // iterator, which would trigger undefined behavior. |
| 695 if (pending_requests_.GetNextHighestIterator() == | 739 if (pending_requests_.GetNextHighestIterator() == |
| 696 pending_requests_.End()) | 740 pending_requests_.End()) |
| 697 break; | 741 break; |
| 698 request_iter = pending_requests_.GetNextHighestIterator(); | 742 request_iter = pending_requests_.GetNextHighestIterator(); |
| 699 } else if (query_result == DO_NOT_START_REQUEST_AND_KEEP_SEARCHING) { | 743 } else if (query_result == DO_NOT_START_REQUEST_AND_KEEP_SEARCHING) { |
| 700 ++request_iter; | 744 ++request_iter; |
| (...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 914 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, | 958 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, |
| 915 new_priority_params); | 959 new_priority_params); |
| 916 } | 960 } |
| 917 | 961 |
| 918 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( | 962 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( |
| 919 int child_id, int route_id) { | 963 int child_id, int route_id) { |
| 920 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; | 964 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; |
| 921 } | 965 } |
| 922 | 966 |
| 923 } // namespace content | 967 } // namespace content |
| OLD | NEW |