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 3 // found in the LICENSE file. 4 5 #include <set> 6 7 #include "content/browser/loader/resource_scheduler.h" 8 9 #include "base/stl_util.h" 10 #include "content/common/resource_messages.h" 11 #include "content/browser/loader/resource_message_delegate.h" 12 #include "content/public/browser/resource_controller.h" 13 #include "content/public/browser/resource_request_info.h" 14 #include "content/public/browser/resource_throttle.h" 15 #include "ipc/ipc_message_macros.h" 16 #include "net/base/host_port_pair.h" 17 #include "net/base/load_flags.h" 18 #include "net/base/request_priority.h" 19 #include "net/http/http_server_properties.h" 20 #include "net/url_request/url_request.h" 21 #include "net/url_request/url_request_context.h" 22 23 namespace content { 24 25 static const size_t kCoalescedTimerPeriod = 5000; 26 static const size_t kMaxNumDelayableRequestsPerClient = 10; 27 static const size_t kMaxNumDelayableRequestsPerHost = 6; 28 static const size_t kMaxNumThrottledRequestsPerClient = 1; 29 30 struct ResourceScheduler::RequestPriorityParams { 31 RequestPriorityParams() 32 : priority(net::DEFAULT_PRIORITY), 33 intra_priority(0) { 34 } 35 36 RequestPriorityParams(net::RequestPriority priority, int intra_priority) 37 : priority(priority), 38 intra_priority(intra_priority) { 39 } 40 41 bool operator==(const RequestPriorityParams& other) const { 42 return (priority == other.priority) && 43 (intra_priority == other.intra_priority); 44 } 45 46 bool operator!=(const RequestPriorityParams& other) const { 47 return !(*this == other); 48 } 49 50 bool GreaterThan(const RequestPriorityParams& other) const { 51 if (priority != other.priority) 52 return priority > other.priority; 53 return intra_priority > other.intra_priority; 54 } 55 56 net::RequestPriority priority; 57 int intra_priority; 58 }; 59 60 class ResourceScheduler::RequestQueue { 61 public: 62 typedef std::multiset<ScheduledResourceRequest*, ScheduledResourceSorter> 63 NetQueue; 64 65 RequestQueue() : fifo_ordering_ids_(0) {} 66 ~RequestQueue() {} 67 68 // Adds |request| to the queue with given |priority|. 69 void Insert(ScheduledResourceRequest* request); 70 71 // Removes |request| from the queue. 72 void Erase(ScheduledResourceRequest* request) { 73 PointerMap::iterator it = pointers_.find(request); 74 DCHECK(it != pointers_.end()); 75 if (it == pointers_.end()) 76 return; 77 queue_.erase(it->second); 78 pointers_.erase(it); 79 } 80 81 NetQueue::iterator GetNextHighestIterator() { 82 return queue_.begin(); 83 } 84 85 NetQueue::iterator End() { 86 return queue_.end(); 87 } 88 89 // Returns true if |request| is queued. 90 bool IsQueued(ScheduledResourceRequest* request) const { 91 return ContainsKey(pointers_, request); 92 } 93 94 // Returns true if no requests are queued. 95 bool IsEmpty() const { return queue_.size() == 0; } 96 97 private: 98 typedef std::map<ScheduledResourceRequest*, NetQueue::iterator> PointerMap; 99 100 uint32 MakeFifoOrderingId() { 101 fifo_ordering_ids_ += 1; 102 return fifo_ordering_ids_; 103 } 104 105 // Used to create an ordering ID for scheduled resources so that resources 106 // with same priority/intra_priority stay in fifo order. 107 uint32 fifo_ordering_ids_; 108 109 NetQueue queue_; 110 PointerMap pointers_; 111 }; 112 113 // This is the handle we return to the ResourceDispatcherHostImpl so it can 114 // interact with the request. 115 class ResourceScheduler::ScheduledResourceRequest 116 : public ResourceMessageDelegate, 117 public ResourceThrottle { 118 public: 119 ScheduledResourceRequest(const ClientId& client_id, 120 net::URLRequest* request, 121 ResourceScheduler* scheduler, 122 const RequestPriorityParams& priority) 123 : ResourceMessageDelegate(request), 124 client_id_(client_id), 125 request_(request), 126 ready_(false), 127 deferred_(false), 128 classification_(NORMAL_REQUEST), 129 scheduler_(scheduler), 130 priority_(priority), 131 fifo_ordering_(0) { 132 TRACE_EVENT_ASYNC_BEGIN1("net", "URLRequest", request_, 133 "url", request->url().spec()); 134 } 135 136 virtual ~ScheduledResourceRequest() { 137 scheduler_->RemoveRequest(this); 138 } 139 140 void Start() { 141 TRACE_EVENT_ASYNC_STEP_PAST0("net", "URLRequest", request_, "Queued"); 142 ready_ = true; 143 if (deferred_ && request_->status().is_success()) { 144 deferred_ = false; 145 controller()->Resume(); 146 } 147 } 148 149 void set_request_priority_params(const RequestPriorityParams& priority) { 150 priority_ = priority; 151 } 152 const RequestPriorityParams& get_request_priority_params() const { 153 return priority_; 154 } 155 const ClientId& client_id() const { return client_id_; } 156 net::URLRequest* url_request() { return request_; } 157 const net::URLRequest* url_request() const { return request_; } 158 uint32 fifo_ordering() const { return fifo_ordering_; } 159 void set_fifo_ordering(uint32 fifo_ordering) { 160 fifo_ordering_ = fifo_ordering; 161 } 162 RequestClassification classification() const { 163 return classification_; 164 } 165 void set_classification(RequestClassification classification) { 166 classification_ = classification; 167 } 168 169 private: 170 // ResourceMessageDelegate interface: 171 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 172 bool handled = true; 173 IPC_BEGIN_MESSAGE_MAP(ScheduledResourceRequest, message) 174 IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority) 175 IPC_MESSAGE_UNHANDLED(handled = false) 176 IPC_END_MESSAGE_MAP() 177 return handled; 178 } 179 180 // ResourceThrottle interface: 181 virtual void WillStartRequest(bool* defer) OVERRIDE { 182 deferred_ = *defer = !ready_; 183 } 184 185 virtual const char* GetNameForLogging() const OVERRIDE { 186 return "ResourceScheduler"; 187 } 188 189 void DidChangePriority(int request_id, net::RequestPriority new_priority, 190 int intra_priority_value) { 191 scheduler_->ReprioritizeRequest(this, new_priority, intra_priority_value); 192 } 193 194 ClientId client_id_; 195 net::URLRequest* request_; 196 bool ready_; 197 bool deferred_; 198 RequestClassification classification_; 199 ResourceScheduler* scheduler_; 200 RequestPriorityParams priority_; 201 uint32 fifo_ordering_; 202 203 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest); 204 }; 205 206 bool ResourceScheduler::ScheduledResourceSorter::operator()( 207 const ScheduledResourceRequest* a, 208 const ScheduledResourceRequest* b) const { 209 // Want the set to be ordered first by decreasing priority, then by 210 // decreasing intra_priority. 211 // ie. with (priority, intra_priority) 212 // [(1, 0), (1, 0), (0, 100), (0, 0)] 213 if (a->get_request_priority_params() != b->get_request_priority_params()) 214 return a->get_request_priority_params().GreaterThan( 215 b->get_request_priority_params()); 216 217 // If priority/intra_priority is the same, fall back to fifo ordering. 218 // std::multiset doesn't guarantee this until c++11. 219 return a->fifo_ordering() < b->fifo_ordering(); 220 } 221 222 void ResourceScheduler::RequestQueue::Insert( 223 ScheduledResourceRequest* request) { 224 DCHECK(!ContainsKey(pointers_, request)); 225 request->set_fifo_ordering(MakeFifoOrderingId()); 226 pointers_[request] = queue_.insert(request); 227 } 228 229 // Each client represents a tab. 230 class ResourceScheduler::Client { 231 public: 232 explicit Client(ResourceScheduler* scheduler, bool is_visible) 233 : is_audible_(false), 234 is_visible_(is_visible), 235 is_loaded_(false), 236 is_paused_(false), 237 has_body_(false), 238 using_spdy_proxy_(false), 239 in_flight_delayable_count_(0), 240 total_layout_blocking_count_(0), 241 throttle_state_(ResourceScheduler::THROTTLED) { 242 scheduler_ = scheduler; 243 } 244 245 ~Client() { 246 // Update to default state and pause to ensure the scheduler has a 247 // correct count of relevant types of clients. 248 is_visible_ = false; 249 is_audible_ = false; 250 is_paused_ = true; 251 UpdateThrottleState(); 252 } 253 254 void ScheduleRequest( 255 net::URLRequest* url_request, 256 ScheduledResourceRequest* request) { 257 if (ShouldStartRequest(request) == START_REQUEST) 258 StartRequest(request); 259 else 260 pending_requests_.Insert(request); 261 SetRequestClassification(request, ClassifyRequest(request)); 262 } 263 264 void RemoveRequest(ScheduledResourceRequest* request) { 265 if (pending_requests_.IsQueued(request)) { 266 pending_requests_.Erase(request); 267 DCHECK(!ContainsKey(in_flight_requests_, request)); 268 } else { 269 EraseInFlightRequest(request); 270 271 // Removing this request may have freed up another to load. 272 LoadAnyStartablePendingRequests(); 273 } 274 } 275 276 RequestSet RemoveAllRequests() { 277 RequestSet unowned_requests; 278 for (RequestSet::iterator it = in_flight_requests_.begin(); 279 it != in_flight_requests_.end(); ++it) { 280 unowned_requests.insert(*it); 281 (*it)->set_classification(NORMAL_REQUEST); 282 } 283 ClearInFlightRequests(); 284 return unowned_requests; 285 } 286 287 bool is_active() const { return is_visible_ || is_audible_; } 288 289 bool is_loaded() const { return is_loaded_; } 290 291 bool is_visible() const { return is_visible_; } 292 293 void OnAudibilityChanged(bool is_audible) { 294 if (is_audible == is_audible_) { 295 return; 296 } 297 is_audible_ = is_audible; 298 UpdateThrottleState(); 299 } 300 301 void OnVisibilityChanged(bool is_visible) { 302 if (is_visible == is_visible_) { 303 return; 304 } 305 is_visible_ = is_visible; 306 UpdateThrottleState(); 307 } 308 309 void OnLoadingStateChanged(bool is_loaded) { 310 if (is_loaded == is_loaded_) { 311 return; 312 } 313 is_loaded_ = is_loaded; 314 UpdateThrottleState(); 315 } 316 317 void SetPaused() { 318 is_paused_ = true; 319 UpdateThrottleState(); 320 } 321 322 void UpdateThrottleState() { 323 ClientThrottleState old_throttle_state = throttle_state_; 324 if (!scheduler_->should_throttle()) { 325 SetThrottleState(UNTHROTTLED); 326 } else if (is_active() && !is_loaded_) { 327 SetThrottleState(ACTIVE_AND_LOADING); 328 } else if (is_active()) { 329 SetThrottleState(UNTHROTTLED); 330 } else if (is_paused_) { 331 SetThrottleState(PAUSED); 332 } else if (!scheduler_->active_clients_loaded()) { 333 SetThrottleState(THROTTLED); 334 } else if (is_loaded_ && scheduler_->should_coalesce()) { 335 SetThrottleState(COALESCED); 336 } else if (!is_active()) { 337 SetThrottleState(UNTHROTTLED); 338 } 339 340 if (throttle_state_ == old_throttle_state) { 341 return; 342 } 343 if (throttle_state_ == ACTIVE_AND_LOADING) { 344 scheduler_->IncrementActiveClientsLoading(); 345 } else if (old_throttle_state == ACTIVE_AND_LOADING) { 346 scheduler_->DecrementActiveClientsLoading(); 347 } 348 if (throttle_state_ == COALESCED) { 349 scheduler_->IncrementCoalescedClients(); 350 } else if (old_throttle_state == COALESCED) { 351 scheduler_->DecrementCoalescedClients(); 352 } 353 } 354 355 void OnNavigate() { 356 has_body_ = false; 357 is_loaded_ = false; 358 } 359 360 void OnWillInsertBody() { 361 has_body_ = true; 362 LoadAnyStartablePendingRequests(); 363 } 364 365 void OnReceivedSpdyProxiedHttpResponse() { 366 if (!using_spdy_proxy_) { 367 using_spdy_proxy_ = true; 368 LoadAnyStartablePendingRequests(); 369 } 370 } 371 372 void ReprioritizeRequest(ScheduledResourceRequest* request, 373 RequestPriorityParams old_priority_params, 374 RequestPriorityParams new_priority_params) { 375 request->url_request()->SetPriority(new_priority_params.priority); 376 request->set_request_priority_params(new_priority_params); 377 if (!pending_requests_.IsQueued(request)) { 378 DCHECK(ContainsKey(in_flight_requests_, request)); 379 // The priority and SPDY support may have changed, so update the 380 // delayable count. 381 SetRequestClassification(request, ClassifyRequest(request)); 382 // Request has already started. 383 return; 384 } 385 386 pending_requests_.Erase(request); 387 pending_requests_.Insert(request); 388 389 if (new_priority_params.priority > old_priority_params.priority) { 390 // Check if this request is now able to load at its new priority. 391 LoadAnyStartablePendingRequests(); 392 } 393 } 394 395 // Called on Client creation, when a Client changes user observability, 396 // possibly when all observable Clients have finished loading, and 397 // possibly when this Client has finished loading. 398 // State changes: 399 // Client became observable. 400 // any state -> UNTHROTTLED 401 // Client is unobservable, but all observable clients finished loading. 402 // THROTTLED -> UNTHROTTLED 403 // Non-observable client finished loading. 404 // THROTTLED || UNTHROTTLED -> COALESCED 405 // Non-observable client, an observable client starts loading. 406 // COALESCED -> THROTTLED 407 // A COALESCED client will transition into UNTHROTTLED when the network is 408 // woken up by a heartbeat and then transition back into COALESCED. 409 void SetThrottleState(ResourceScheduler::ClientThrottleState throttle_state) { 410 if (throttle_state == throttle_state_) { 411 return; 412 } 413 throttle_state_ = throttle_state; 414 if (throttle_state_ != PAUSED) { 415 is_paused_ = false; 416 } 417 LoadAnyStartablePendingRequests(); 418 // TODO(aiolos): Stop any started but not inflght requests when 419 // switching to stricter throttle state? 420 } 421 422 ResourceScheduler::ClientThrottleState throttle_state() const { 423 return throttle_state_; 424 } 425 426 void LoadCoalescedRequests() { 427 if (throttle_state_ != COALESCED) { 428 return; 429 } 430 if (scheduler_->active_clients_loaded()) { 431 SetThrottleState(UNTHROTTLED); 432 } else { 433 SetThrottleState(THROTTLED); 434 } 435 LoadAnyStartablePendingRequests(); 436 SetThrottleState(COALESCED); 437 } 438 439 private: 440 enum ShouldStartReqResult { 441 DO_NOT_START_REQUEST_AND_STOP_SEARCHING, 442 DO_NOT_START_REQUEST_AND_KEEP_SEARCHING, 443 START_REQUEST, 444 }; 445 446 void InsertInFlightRequest(ScheduledResourceRequest* request) { 447 in_flight_requests_.insert(request); 448 SetRequestClassification(request, ClassifyRequest(request)); 449 } 450 451 void EraseInFlightRequest(ScheduledResourceRequest* request) { 452 size_t erased = in_flight_requests_.erase(request); 453 DCHECK_EQ(1u, erased); 454 // Clear any special state that we were tracking for this request. 455 SetRequestClassification(request, NORMAL_REQUEST); 456 } 457 458 void ClearInFlightRequests() { 459 in_flight_requests_.clear(); 460 in_flight_delayable_count_ = 0; 461 total_layout_blocking_count_ = 0; 462 } 463 464 size_t CountRequestsWithClassification( 465 const RequestClassification classification, const bool include_pending) { 466 size_t classification_request_count = 0; 467 for (RequestSet::const_iterator it = in_flight_requests_.begin(); 468 it != in_flight_requests_.end(); ++it) { 469 if ((*it)->classification() == classification) 470 classification_request_count++; 471 } 472 if (include_pending) { 473 for (RequestQueue::NetQueue::const_iterator 474 it = pending_requests_.GetNextHighestIterator(); 475 it != pending_requests_.End(); ++it) { 476 if ((*it)->classification() == classification) 477 classification_request_count++; 478 } 479 } 480 return classification_request_count; 481 } 482 483 void SetRequestClassification(ScheduledResourceRequest* request, 484 RequestClassification classification) { 485 RequestClassification old_classification = request->classification(); 486 if (old_classification == classification) 487 return; 488 489 if (old_classification == IN_FLIGHT_DELAYABLE_REQUEST) 490 in_flight_delayable_count_--; 491 if (old_classification == LAYOUT_BLOCKING_REQUEST) 492 total_layout_blocking_count_--; 493 494 if (classification == IN_FLIGHT_DELAYABLE_REQUEST) 495 in_flight_delayable_count_++; 496 if (classification == LAYOUT_BLOCKING_REQUEST) 497 total_layout_blocking_count_++; 498 499 request->set_classification(classification); 500 DCHECK_EQ( 501 CountRequestsWithClassification(IN_FLIGHT_DELAYABLE_REQUEST, false), 502 in_flight_delayable_count_); 503 DCHECK_EQ(CountRequestsWithClassification(LAYOUT_BLOCKING_REQUEST, true), 504 total_layout_blocking_count_); 505 } 506 507 RequestClassification ClassifyRequest(ScheduledResourceRequest* request) { 508 // If a request is already marked as layout-blocking make sure to keep the 509 // classification across redirects unless the priority was lowered. 510 if (request->classification() == LAYOUT_BLOCKING_REQUEST && 511 request->url_request()->priority() >= net::LOW) { 512 return LAYOUT_BLOCKING_REQUEST; 513 } 514 515 if (!has_body_ && request->url_request()->priority() >= net::LOW) 516 return LAYOUT_BLOCKING_REQUEST; 517 518 if (request->url_request()->priority() < net::LOW) { 519 net::HostPortPair host_port_pair = 520 net::HostPortPair::FromURL(request->url_request()->url()); 521 net::HttpServerProperties& http_server_properties = 522 *request->url_request()->context()->http_server_properties(); 523 if (!http_server_properties.SupportsSpdy(host_port_pair) && 524 ContainsKey(in_flight_requests_, request)) { 525 return IN_FLIGHT_DELAYABLE_REQUEST; 526 } 527 } 528 return NORMAL_REQUEST; 529 } 530 531 bool ShouldKeepSearching( 532 const net::HostPortPair& active_request_host) const { 533 size_t same_host_count = 0; 534 for (RequestSet::const_iterator it = in_flight_requests_.begin(); 535 it != in_flight_requests_.end(); ++it) { 536 net::HostPortPair host_port_pair = 537 net::HostPortPair::FromURL((*it)->url_request()->url()); 538 if (active_request_host.Equals(host_port_pair)) { 539 same_host_count++; 540 if (same_host_count >= kMaxNumDelayableRequestsPerHost) 541 return true; 542 } 543 } 544 return false; 545 } 546 547 void StartRequest(ScheduledResourceRequest* request) { 548 InsertInFlightRequest(request); 549 request->Start(); 550 } 551 552 // ShouldStartRequest is the main scheduling algorithm. 553 // 554 // Requests are evaluated on five attributes: 555 // 556 // 1. Non-delayable requests: 557 // * Synchronous requests. 558 // * Non-HTTP[S] requests. 559 // 560 // 2. Requests to SPDY-capable origin servers. 561 // 562 // 3. High-priority requests: 563 // * Higher priority requests (>= net::LOW). 564 // 565 // 4. Layout-blocking requests: 566 // * High-priority requests initiated before the renderer has a <body>. 567 // 568 // 5. Low priority requests 569 // 570 // The following rules are followed: 571 // 572 // ACTIVE_AND_LOADING and UNTHROTTLED Clients follow these rules: 573 // * Non-delayable, High-priority and SPDY capable requests are issued 574 // immediately. 575 // * Low priority requests are delayable. 576 // * Allow one delayable request to load at a time while layout-blocking 577 // requests are loading. 578 // * If no high priority or layout-blocking requests are in flight, start 579 // loading delayable requests. 580 // * Never exceed 10 delayable requests in flight per client. 581 // * Never exceed 6 delayable requests for a given host. 582 // 583 // THROTTLED Clients follow these rules: 584 // * Non-delayable and SPDY-capable requests are issued immediately. 585 // * At most one non-SPDY request will be issued per THROTTLED Client 586 // * If no high priority requests are in flight, start loading low priority 587 // requests. 588 // 589 // COALESCED Clients never load requests, with the following exceptions: 590 // * Non-delayable requests are issued imediately. 591 // * On a (currently 5 second) heart beat, they load all requests as an 592 // UNTHROTTLED Client, and then return to the COALESCED state. 593 // * When an active Client makes a request, they are THROTTLED until the 594 // active Client finishes loading. 595 ShouldStartReqResult ShouldStartRequest( 596 ScheduledResourceRequest* request) const { 597 const net::URLRequest& url_request = *request->url_request(); 598 // Syncronous requests could block the entire render, which could impact 599 // user-observable Clients. 600 if (!ResourceRequestInfo::ForRequest(&url_request)->IsAsync()) { 601 return START_REQUEST; 602 } 603 604 // TODO(simonjam): This may end up causing disk contention. We should 605 // experiment with throttling if that happens. 606 // TODO(aiolos): We probably want to Coalesce these as well to avoid 607 // waking the disk. 608 if (!url_request.url().SchemeIsHTTPOrHTTPS()) { 609 return START_REQUEST; 610 } 611 612 if (throttle_state_ == COALESCED) { 613 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; 614 } 615 616 if (using_spdy_proxy_ && url_request.url().SchemeIs(url::kHttpScheme)) { 617 return START_REQUEST; 618 } 619 620 net::HostPortPair host_port_pair = 621 net::HostPortPair::FromURL(url_request.url()); 622 net::HttpServerProperties& http_server_properties = 623 *url_request.context()->http_server_properties(); 624 625 // TODO(willchan): We should really improve this algorithm as described in 626 // crbug.com/164101. Also, theoretically we should not count a SPDY request 627 // against the delayable requests limit. 628 if (http_server_properties.SupportsSpdy(host_port_pair)) { 629 return START_REQUEST; 630 } 631 632 if (throttle_state_ == THROTTLED && 633 in_flight_requests_.size() >= kMaxNumThrottledRequestsPerClient) { 634 // There may still be SPDY-capable requests that should be issued. 635 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING; 636 } 637 638 // High-priority and layout-blocking requests. 639 if (url_request.priority() >= net::LOW) { 640 return START_REQUEST; 641 } 642 643 if (in_flight_delayable_count_ >= kMaxNumDelayableRequestsPerClient) { 644 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; 645 } 646 647 if (ShouldKeepSearching(host_port_pair)) { 648 // There may be other requests for other hosts we'd allow, 649 // so keep checking. 650 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING; 651 } 652 653 bool have_immediate_requests_in_flight = 654 in_flight_requests_.size() > in_flight_delayable_count_; 655 if (have_immediate_requests_in_flight && 656 total_layout_blocking_count_ != 0 && 657 in_flight_delayable_count_ != 0) { 658 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; 659 } 660 661 return START_REQUEST; 662 } 663 664 void LoadAnyStartablePendingRequests() { 665 // We iterate through all the pending requests, starting with the highest 666 // priority one. For each entry, one of three things can happen: 667 // 1) We start the request, remove it from the list, and keep checking. 668 // 2) We do NOT start the request, but ShouldStartRequest() signals us that 669 // there may be room for other requests, so we keep checking and leave 670 // the previous request still in the list. 671 // 3) We do not start the request, same as above, but StartRequest() tells 672 // us there's no point in checking any further requests. 673 RequestQueue::NetQueue::iterator request_iter = 674 pending_requests_.GetNextHighestIterator(); 675 676 while (request_iter != pending_requests_.End()) { 677 ScheduledResourceRequest* request = *request_iter; 678 ShouldStartReqResult query_result = ShouldStartRequest(request); 679 680 if (query_result == START_REQUEST) { 681 pending_requests_.Erase(request); 682 StartRequest(request); 683 684 // StartRequest can modify the pending list, so we (re)start evaluation 685 // from the currently highest priority request. Avoid copying a singular 686 // iterator, which would trigger undefined behavior. 687 if (pending_requests_.GetNextHighestIterator() == 688 pending_requests_.End()) 689 break; 690 request_iter = pending_requests_.GetNextHighestIterator(); 691 } else if (query_result == DO_NOT_START_REQUEST_AND_KEEP_SEARCHING) { 692 ++request_iter; 693 continue; 694 } else { 695 DCHECK(query_result == DO_NOT_START_REQUEST_AND_STOP_SEARCHING); 696 break; 697 } 698 } 699 } 700 701 bool is_audible_; 702 bool is_visible_; 703 bool is_loaded_; 704 bool is_paused_; 705 bool has_body_; 706 bool using_spdy_proxy_; 707 RequestQueue pending_requests_; 708 RequestSet in_flight_requests_; 709 ResourceScheduler* scheduler_; 710 // The number of delayable in-flight requests. 711 size_t in_flight_delayable_count_; 712 // The number of layout-blocking in-flight requests. 713 size_t total_layout_blocking_count_; 714 ResourceScheduler::ClientThrottleState throttle_state_; 715 }; 716 717 ResourceScheduler::ResourceScheduler() 718 : should_coalesce_(false), 719 should_throttle_(false), 720 active_clients_loading_(0), 721 coalesced_clients_(0), 722 coalescing_timer_(new base::Timer(true /* retain_user_task */, 723 true /* is_repeating */)) { 724 } 725 726 ResourceScheduler::~ResourceScheduler() { 727 DCHECK(unowned_requests_.empty()); 728 DCHECK(client_map_.empty()); 729 } 730 731 void ResourceScheduler::SetThrottleOptionsForTesting(bool should_throttle, 732 bool should_coalesce) { 733 should_coalesce_ = should_coalesce; 734 should_throttle_ = should_throttle; 735 OnLoadingActiveClientsStateChangedForAllClients(); 736 } 737 738 ResourceScheduler::ClientThrottleState 739 ResourceScheduler::GetClientStateForTesting(int child_id, int route_id) { 740 Client* client = GetClient(child_id, route_id); 741 DCHECK(client); 742 return client->throttle_state(); 743 } 744 745 scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( 746 int child_id, 747 int route_id, 748 net::URLRequest* url_request) { 749 DCHECK(CalledOnValidThread()); 750 ClientId client_id = MakeClientId(child_id, route_id); 751 scoped_ptr<ScheduledResourceRequest> request( 752 new ScheduledResourceRequest(client_id, url_request, this, 753 RequestPriorityParams(url_request->priority(), 0))); 754 755 ClientMap::iterator it = client_map_.find(client_id); 756 if (it == client_map_.end()) { 757 // There are several ways this could happen: 758 // 1. <a ping> requests don't have a route_id. 759 // 2. Most unittests don't send the IPCs needed to register Clients. 760 // 3. The tab is closed while a RequestResource IPC is in flight. 761 unowned_requests_.insert(request.get()); 762 request->Start(); 763 return request.PassAs<ResourceThrottle>(); 764 } 765 766 Client* client = it->second; 767 client->ScheduleRequest(url_request, request.get()); 768 return request.PassAs<ResourceThrottle>(); 769 } 770 771 void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) { 772 DCHECK(CalledOnValidThread()); 773 if (ContainsKey(unowned_requests_, request)) { 774 unowned_requests_.erase(request); 775 return; 776 } 777 778 ClientMap::iterator client_it = client_map_.find(request->client_id()); 779 if (client_it == client_map_.end()) { 780 return; 781 } 782 783 Client* client = client_it->second; 784 client->RemoveRequest(request); 785 } 786 787 void ResourceScheduler::OnClientCreated(int child_id, 788 int route_id, 789 bool is_visible) { 790 DCHECK(CalledOnValidThread()); 791 ClientId client_id = MakeClientId(child_id, route_id); 792 DCHECK(!ContainsKey(client_map_, client_id)); 793 794 Client* client = new Client(this, is_visible); 795 client_map_[client_id] = client; 796 797 // TODO(aiolos): set Client visibility/audibility when signals are added 798 // this will UNTHROTTLE Clients as needed 799 client->UpdateThrottleState(); 800 } 801 802 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) { 803 DCHECK(CalledOnValidThread()); 804 ClientId client_id = MakeClientId(child_id, route_id); 805 DCHECK(ContainsKey(client_map_, client_id)); 806 ClientMap::iterator it = client_map_.find(client_id); 807 if (it == client_map_.end()) 808 return; 809 810 Client* client = it->second; 811 // FYI, ResourceDispatcherHost cancels all of the requests after this function 812 // is called. It should end up canceling all of the requests except for a 813 // cross-renderer navigation. 814 RequestSet client_unowned_requests = client->RemoveAllRequests(); 815 for (RequestSet::iterator it = client_unowned_requests.begin(); 816 it != client_unowned_requests.end(); ++it) { 817 unowned_requests_.insert(*it); 818 } 819 820 delete client; 821 client_map_.erase(it); 822 } 823 824 void ResourceScheduler::OnVisibilityChanged(int child_id, 825 int route_id, 826 bool is_visible) { 827 Client* client = GetClient(child_id, route_id); 828 DCHECK(client); 829 client->OnVisibilityChanged(is_visible); 830 } 831 832 void ResourceScheduler::OnLoadingStateChanged(int child_id, 833 int route_id, 834 bool is_loaded) { 835 Client* client = GetClient(child_id, route_id); 836 DCHECK(client); 837 client->OnLoadingStateChanged(is_loaded); 838 } 839 840 void ResourceScheduler::OnNavigate(int child_id, int route_id) { 841 DCHECK(CalledOnValidThread()); 842 ClientId client_id = MakeClientId(child_id, route_id); 843 844 ClientMap::iterator it = client_map_.find(client_id); 845 if (it == client_map_.end()) { 846 // The client was likely deleted shortly before we received this IPC. 847 return; 848 } 849 850 Client* client = it->second; 851 client->OnNavigate(); 852 } 853 854 void ResourceScheduler::OnWillInsertBody(int child_id, int route_id) { 855 DCHECK(CalledOnValidThread()); 856 ClientId client_id = MakeClientId(child_id, route_id); 857 858 ClientMap::iterator it = client_map_.find(client_id); 859 if (it == client_map_.end()) { 860 // The client was likely deleted shortly before we received this IPC. 861 return; 862 } 863 864 Client* client = it->second; 865 client->OnWillInsertBody(); 866 } 867 868 void ResourceScheduler::OnReceivedSpdyProxiedHttpResponse( 869 int child_id, 870 int route_id) { 871 DCHECK(CalledOnValidThread()); 872 ClientId client_id = MakeClientId(child_id, route_id); 873 874 ClientMap::iterator client_it = client_map_.find(client_id); 875 if (client_it == client_map_.end()) { 876 return; 877 } 878 879 Client* client = client_it->second; 880 client->OnReceivedSpdyProxiedHttpResponse(); 881 } 882 883 void ResourceScheduler::OnAudibilityChanged(int child_id, 884 int route_id, 885 bool is_audible) { 886 Client* client = GetClient(child_id, route_id); 887 DCHECK(client); 888 client->OnAudibilityChanged(is_audible); 889 } 890 891 bool ResourceScheduler::IsClientVisibleForTesting(int child_id, int route_id) { 892 Client* client = GetClient(child_id, route_id); 893 DCHECK(client); 894 return client->is_visible(); 895 } 896 897 ResourceScheduler::Client* ResourceScheduler::GetClient(int child_id, 898 int route_id) { 899 ClientId client_id = MakeClientId(child_id, route_id); 900 ClientMap::iterator client_it = client_map_.find(client_id); 901 if (client_it == client_map_.end()) { 902 return NULL; 903 } 904 return client_it->second; 905 } 906 907 void ResourceScheduler::DecrementActiveClientsLoading() { 908 DCHECK_NE(0u, active_clients_loading_); 909 --active_clients_loading_; 910 DCHECK_EQ(active_clients_loading_, CountActiveClientsLoading()); 911 if (active_clients_loading_ == 0) { 912 OnLoadingActiveClientsStateChangedForAllClients(); 913 } 914 } 915 916 void ResourceScheduler::IncrementActiveClientsLoading() { 917 ++active_clients_loading_; 918 DCHECK_EQ(active_clients_loading_, CountActiveClientsLoading()); 919 if (active_clients_loading_ == 1) { 920 OnLoadingActiveClientsStateChangedForAllClients(); 921 } 922 } 923 924 void ResourceScheduler::OnLoadingActiveClientsStateChangedForAllClients() { 925 ClientMap::iterator client_it = client_map_.begin(); 926 while (client_it != client_map_.end()) { 927 Client* client = client_it->second; 928 client->UpdateThrottleState(); 929 ++client_it; 930 } 931 } 932 933 size_t ResourceScheduler::CountActiveClientsLoading() const { 934 size_t active_and_loading = 0; 935 ClientMap::const_iterator client_it = client_map_.begin(); 936 while (client_it != client_map_.end()) { 937 Client* client = client_it->second; 938 if (client->throttle_state() == ACTIVE_AND_LOADING) { 939 ++active_and_loading; 940 } 941 ++client_it; 942 } 943 return active_and_loading; 944 } 945 946 void ResourceScheduler::IncrementCoalescedClients() { 947 ++coalesced_clients_; 948 DCHECK(should_coalesce_); 949 DCHECK_EQ(coalesced_clients_, CountCoalescedClients()); 950 if (coalesced_clients_ == 1) { 951 coalescing_timer_->Start( 952 FROM_HERE, 953 base::TimeDelta::FromMilliseconds(kCoalescedTimerPeriod), 954 base::Bind(&ResourceScheduler::LoadCoalescedRequests, 955 base::Unretained(this))); 956 } 957 } 958 959 void ResourceScheduler::DecrementCoalescedClients() { 960 DCHECK(should_coalesce_); 961 DCHECK_NE(0U, coalesced_clients_); 962 --coalesced_clients_; 963 DCHECK_EQ(coalesced_clients_, CountCoalescedClients()); 964 if (coalesced_clients_ == 0) { 965 coalescing_timer_->Stop(); 966 } 967 } 968 969 size_t ResourceScheduler::CountCoalescedClients() const { 970 DCHECK(should_coalesce_); 971 size_t coalesced_clients = 0; 972 ClientMap::const_iterator client_it = client_map_.begin(); 973 while (client_it != client_map_.end()) { 974 Client* client = client_it->second; 975 if (client->throttle_state() == COALESCED) { 976 ++coalesced_clients; 977 } 978 ++client_it; 979 } 980 return coalesced_clients_; 981 } 982 983 void ResourceScheduler::LoadCoalescedRequests() { 984 DCHECK(should_coalesce_); 985 ClientMap::iterator client_it = client_map_.begin(); 986 while (client_it != client_map_.end()) { 987 Client* client = client_it->second; 988 client->LoadCoalescedRequests(); 989 ++client_it; 990 } 991 } 992 993 void ResourceScheduler::ReprioritizeRequest(ScheduledResourceRequest* request, 994 net::RequestPriority new_priority, 995 int new_intra_priority_value) { 996 if (request->url_request()->load_flags() & net::LOAD_IGNORE_LIMITS) { 997 // We should not be re-prioritizing requests with the 998 // IGNORE_LIMITS flag. 999 NOTREACHED(); 1000 return; 1001 } 1002 RequestPriorityParams new_priority_params(new_priority, 1003 new_intra_priority_value); 1004 RequestPriorityParams old_priority_params = 1005 request->get_request_priority_params(); 1006 1007 DCHECK(old_priority_params != new_priority_params); 1008 1009 ClientMap::iterator client_it = client_map_.find(request->client_id()); 1010 if (client_it == client_map_.end()) { 1011 // The client was likely deleted shortly before we received this IPC. 1012 request->url_request()->SetPriority(new_priority_params.priority); 1013 request->set_request_priority_params(new_priority_params); 1014 return; 1015 } 1016 1017 if (old_priority_params == new_priority_params) 1018 return; 1019 1020 Client *client = client_it->second; 1021 client->ReprioritizeRequest( 1022 request, old_priority_params, new_priority_params); 1023 } 1024 1025 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( 1026 int child_id, int route_id) { 1027 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; 1028 } 1029 1030 } // namespace content 1031