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) { |
| 602 DCHECK_NE(RequestStartTrigger::NONE, trigger); |
| 603 request->url_request()->net_log().AddEvent( |
| 604 net::NetLogEventType::RESOURCE_SCHEDULER_REQUEST_STARTED, |
| 605 net::NetLog::StringCallback( |
| 606 "trigger", RequestStartTriggerString(trigger))); |
| 607 } |
563 InsertInFlightRequest(request); | 608 InsertInFlightRequest(request); |
564 request->Start(start_mode); | 609 request->Start(start_mode); |
565 } | 610 } |
566 | 611 |
567 // ShouldStartRequest is the main scheduling algorithm. | 612 // ShouldStartRequest is the main scheduling algorithm. |
568 // | 613 // |
569 // Requests are evaluated on five attributes: | 614 // Requests are evaluated on five attributes: |
570 // | 615 // |
571 // 1. Non-delayable requests: | 616 // 1. Non-delayable requests: |
572 // * Synchronous requests. | 617 // * 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 | 707 // Block the request if at least one request is in flight and the |
663 // number of in-flight delayable requests has hit the configured | 708 // number of in-flight delayable requests has hit the configured |
664 // limit. | 709 // limit. |
665 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | 710 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; |
666 } | 711 } |
667 } | 712 } |
668 | 713 |
669 return START_REQUEST; | 714 return START_REQUEST; |
670 } | 715 } |
671 | 716 |
672 void LoadAnyStartablePendingRequests() { | 717 void LoadAnyStartablePendingRequests(RequestStartTrigger trigger) { |
673 // We iterate through all the pending requests, starting with the highest | 718 // We iterate through all the pending requests, starting with the highest |
674 // priority one. For each entry, one of three things can happen: | 719 // 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. | 720 // 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 | 721 // 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 | 722 // there may be room for other requests, so we keep checking and leave |
678 // the previous request still in the list. | 723 // the previous request still in the list. |
679 // 3) We do not start the request, same as above, but StartRequest() tells | 724 // 3) We do not start the request, same as above, but StartRequest() tells |
680 // us there's no point in checking any further requests. | 725 // us there's no point in checking any further requests. |
681 RequestQueue::NetQueue::iterator request_iter = | 726 RequestQueue::NetQueue::iterator request_iter = |
682 pending_requests_.GetNextHighestIterator(); | 727 pending_requests_.GetNextHighestIterator(); |
683 | 728 |
684 while (request_iter != pending_requests_.End()) { | 729 while (request_iter != pending_requests_.End()) { |
685 ScheduledResourceRequest* request = *request_iter; | 730 ScheduledResourceRequest* request = *request_iter; |
686 ShouldStartReqResult query_result = ShouldStartRequest(request); | 731 ShouldStartReqResult query_result = ShouldStartRequest(request); |
687 | 732 |
688 if (query_result == START_REQUEST) { | 733 if (query_result == START_REQUEST) { |
689 pending_requests_.Erase(request); | 734 pending_requests_.Erase(request); |
690 StartRequest(request, START_ASYNC); | 735 StartRequest(request, START_ASYNC, trigger); |
691 | 736 |
692 // StartRequest can modify the pending list, so we (re)start evaluation | 737 // StartRequest can modify the pending list, so we (re)start evaluation |
693 // from the currently highest priority request. Avoid copying a singular | 738 // from the currently highest priority request. Avoid copying a singular |
694 // iterator, which would trigger undefined behavior. | 739 // iterator, which would trigger undefined behavior. |
695 if (pending_requests_.GetNextHighestIterator() == | 740 if (pending_requests_.GetNextHighestIterator() == |
696 pending_requests_.End()) | 741 pending_requests_.End()) |
697 break; | 742 break; |
698 request_iter = pending_requests_.GetNextHighestIterator(); | 743 request_iter = pending_requests_.GetNextHighestIterator(); |
699 } else if (query_result == DO_NOT_START_REQUEST_AND_KEEP_SEARCHING) { | 744 } else if (query_result == DO_NOT_START_REQUEST_AND_KEEP_SEARCHING) { |
700 ++request_iter; | 745 ++request_iter; |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
914 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, | 959 client->ReprioritizeRequest(scheduled_resource_request, old_priority_params, |
915 new_priority_params); | 960 new_priority_params); |
916 } | 961 } |
917 | 962 |
918 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( | 963 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( |
919 int child_id, int route_id) { | 964 int child_id, int route_id) { |
920 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; | 965 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; |
921 } | 966 } |
922 | 967 |
923 } // namespace content | 968 } // namespace content |
OLD | NEW |