Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(41)

Side by Side Diff: content/browser/loader/resource_scheduler.cc

Issue 23620058: Add a cap of six in-flight requests per host to the ResourceScheduler (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Review fixes Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 "base/stl_util.h" 7 #include "base/stl_util.h"
8 #include "content/common/resource_messages.h" 8 #include "content/common/resource_messages.h"
9 #include "content/browser/loader/resource_message_delegate.h" 9 #include "content/browser/loader/resource_message_delegate.h"
10 #include "content/public/browser/resource_controller.h" 10 #include "content/public/browser/resource_controller.h"
11 #include "content/public/browser/resource_request_info.h" 11 #include "content/public/browser/resource_request_info.h"
12 #include "content/public/browser/resource_throttle.h" 12 #include "content/public/browser/resource_throttle.h"
13 #include "ipc/ipc_message_macros.h" 13 #include "ipc/ipc_message_macros.h"
14 #include "net/base/host_port_pair.h" 14 #include "net/base/host_port_pair.h"
15 #include "net/base/load_flags.h" 15 #include "net/base/load_flags.h"
16 #include "net/base/request_priority.h" 16 #include "net/base/request_priority.h"
17 #include "net/http/http_server_properties.h" 17 #include "net/http/http_server_properties.h"
18 #include "net/url_request/url_request.h" 18 #include "net/url_request/url_request.h"
19 #include "net/url_request/url_request_context.h" 19 #include "net/url_request/url_request_context.h"
20 20
21 namespace content { 21 namespace content {
22 22
23 static const size_t kMaxNumDelayableRequestsPerClient = 10; 23 static const size_t kMaxNumDelayableRequestsPerClient = 10;
24 static const size_t kMaxNumDelayableRequestsPerHost = 6;
24 25
25 // A thin wrapper around net::PriorityQueue that deals with 26 // A thin wrapper around net::PriorityQueue that deals with
26 // ScheduledResourceRequests instead of PriorityQueue::Pointers. 27 // ScheduledResourceRequests instead of PriorityQueue::Pointers.
27 class ResourceScheduler::RequestQueue { 28 class ResourceScheduler::RequestQueue {
29 private:
30 typedef net::PriorityQueue<ScheduledResourceRequest*> NetQueue;
31
28 public: 32 public:
33 class Iterator {
34 public:
35 Iterator(NetQueue* queue) : queue_(queue) {
36 DCHECK(queue != NULL);
37 current_pointer_ = queue_->FirstMax();
38 }
39
40 Iterator& operator++() {
41 current_pointer_ = queue_->NextHighest(current_pointer_);
42 return *this;
43 }
44
45 Iterator operator++(int) {
46 Iterator result(*this);
47 ++(*this);
48 return result;
49 }
50
51 ScheduledResourceRequest* value() {
52 return current_pointer_.value();
53 }
54
55 bool is_null() {
56 return current_pointer_.is_null();
57 }
58
59 private:
60 NetQueue* queue_;
61 NetQueue::Pointer current_pointer_;
62 };
63
29 RequestQueue() : queue_(net::NUM_PRIORITIES) {} 64 RequestQueue() : queue_(net::NUM_PRIORITIES) {}
30 ~RequestQueue() {} 65 ~RequestQueue() {}
31 66
32 // Adds |request| to the queue with given |priority|. 67 // Adds |request| to the queue with given |priority|.
33 void Insert(ScheduledResourceRequest* request, 68 void Insert(ScheduledResourceRequest* request,
34 net::RequestPriority priority) { 69 net::RequestPriority priority) {
35 DCHECK(!ContainsKey(pointers_, request)); 70 DCHECK(!ContainsKey(pointers_, request));
36 NetQueue::Pointer pointer = queue_.Insert(request, priority); 71 NetQueue::Pointer pointer = queue_.Insert(request, priority);
37 pointers_[request] = pointer; 72 pointers_[request] = pointer;
38 } 73 }
39 74
40 // Removes |request| from the queue. 75 // Removes |request| from the queue.
41 void Erase(ScheduledResourceRequest* request) { 76 void Erase(ScheduledResourceRequest* request) {
42 PointerMap::iterator it = pointers_.find(request); 77 PointerMap::iterator it = pointers_.find(request);
43 DCHECK(it != pointers_.end()); 78 DCHECK(it != pointers_.end());
44 queue_.Erase(it->second); 79 queue_.Erase(it->second);
45 pointers_.erase(it); 80 pointers_.erase(it);
46 } 81 }
47 82
48 // Returns the highest priority request that's queued, or NULL if none are. 83 // Returns the highest priority request that's queued, or NULL if none are.
49 ScheduledResourceRequest* FirstMax() { 84 ScheduledResourceRequest* FirstMax() {
50 return queue_.FirstMax().value(); 85 return queue_.FirstMax().value();
51 } 86 }
52 87
88 Iterator GetNextHighestIterator() {
89 return Iterator(&queue_);
90 }
91
53 // Returns true if |request| is queued. 92 // Returns true if |request| is queued.
54 bool IsQueued(ScheduledResourceRequest* request) const { 93 bool IsQueued(ScheduledResourceRequest* request) const {
55 return ContainsKey(pointers_, request); 94 return ContainsKey(pointers_, request);
56 } 95 }
57 96
58 // Returns true if no requests are queued. 97 // Returns true if no requests are queued.
59 bool IsEmpty() const { return queue_.size() == 0; } 98 bool IsEmpty() const { return queue_.size() == 0; }
60 99
61 private: 100 private:
62 typedef net::PriorityQueue<ScheduledResourceRequest*> NetQueue;
63 typedef std::map<ScheduledResourceRequest*, NetQueue::Pointer> PointerMap; 101 typedef std::map<ScheduledResourceRequest*, NetQueue::Pointer> PointerMap;
64 102
65 NetQueue queue_; 103 NetQueue queue_;
66 PointerMap pointers_; 104 PointerMap pointers_;
67 }; 105 };
68 106
69 // This is the handle we return to the ResourceDispatcherHostImpl so it can 107 // This is the handle we return to the ResourceDispatcherHostImpl so it can
70 // interact with the request. 108 // interact with the request.
71 class ResourceScheduler::ScheduledResourceRequest 109 class ResourceScheduler::ScheduledResourceRequest
72 : public ResourceMessageDelegate, 110 : public ResourceMessageDelegate,
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
164 // There are several ways this could happen: 202 // There are several ways this could happen:
165 // 1. <a ping> requests don't have a route_id. 203 // 1. <a ping> requests don't have a route_id.
166 // 2. Most unittests don't send the IPCs needed to register Clients. 204 // 2. Most unittests don't send the IPCs needed to register Clients.
167 // 3. The tab is closed while a RequestResource IPC is in flight. 205 // 3. The tab is closed while a RequestResource IPC is in flight.
168 unowned_requests_.insert(request.get()); 206 unowned_requests_.insert(request.get());
169 request->Start(); 207 request->Start();
170 return request.PassAs<ResourceThrottle>(); 208 return request.PassAs<ResourceThrottle>();
171 } 209 }
172 210
173 Client* client = it->second; 211 Client* client = it->second;
174 if (ShouldStartRequest(request.get(), client)) { 212 if (ShouldStartRequest(request.get(), client) == START_REQUEST) {
175 StartRequest(request.get(), client); 213 StartRequest(request.get(), client);
176 } else { 214 } else {
177 client->pending_requests.Insert(request.get(), url_request->priority()); 215 client->pending_requests.Insert(request.get(), url_request->priority());
178 } 216 }
179 return request.PassAs<ResourceThrottle>(); 217 return request.PassAs<ResourceThrottle>();
180 } 218 }
181 219
182 void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) { 220 void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) {
183 DCHECK(CalledOnValidThread()); 221 DCHECK(CalledOnValidThread());
184 if (ContainsKey(unowned_requests_, request)) { 222 if (ContainsKey(unowned_requests_, request)) {
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
299 client->pending_requests.Insert(request, 337 client->pending_requests.Insert(request,
300 request->url_request()->priority()); 338 request->url_request()->priority());
301 339
302 if (new_priority > old_priority) { 340 if (new_priority > old_priority) {
303 // Check if this request is now able to load at its new priority. 341 // Check if this request is now able to load at its new priority.
304 LoadAnyStartablePendingRequests(client); 342 LoadAnyStartablePendingRequests(client);
305 } 343 }
306 } 344 }
307 345
308 void ResourceScheduler::LoadAnyStartablePendingRequests(Client* client) { 346 void ResourceScheduler::LoadAnyStartablePendingRequests(Client* client) {
309 while (!client->pending_requests.IsEmpty()) { 347 // We iterate through all the pending requests, starting with the highest
310 ScheduledResourceRequest* request = client->pending_requests.FirstMax(); 348 // priority one. For each entry, one of three things can happen:
311 if (ShouldStartRequest(request, client)) { 349 // 1) We start the request, remove it from the list, and keep checking.
350 // 2) We do NOT start the request, but ShouldStartRequest() signals us that
351 // there may be room for other requests, so we keep checking and leave
352 // the previous request still in the list.
353 // 3) We do not start the request, same as above, but StartRequest() tells
354 // us there's no point in checking any further requests.
355
356 RequestQueue::Iterator request_iter =
357 client->pending_requests.GetNextHighestIterator();
358
359 while (!request_iter.is_null()) {
360 ScheduledResourceRequest* request = request_iter.value();
361 ShouldStartReqResult query_result = ShouldStartRequest(request, client);
362
363 if (query_result == START_REQUEST) {
312 client->pending_requests.Erase(request); 364 client->pending_requests.Erase(request);
313 StartRequest(request, client); 365 StartRequest(request, client);
366
367 // StartRequest can modify the pending list, so we (re)start evaluation
368 // from the currently highest priority request.
369 request_iter = client->pending_requests.GetNextHighestIterator();
370 } else if (query_result == DO_NOT_START_REQUEST__KEEP_SEARCHING) {
371 ++request_iter;
372 continue;
314 } else { 373 } else {
374 DCHECK(query_result == DO_NOT_START_REQUEST__STOP_SEARCHING);
315 break; 375 break;
316 } 376 }
317 } 377 }
318 } 378 }
319 379
320 size_t ResourceScheduler::GetNumDelayableRequestsInFlight( 380 void ResourceScheduler::GetNumDelayableRequestsInFlight(
321 Client* client) const { 381 Client* client,
322 size_t count = 0; 382 const net::HostPortPair& active_request_host,
383 size_t* total_delayable,
384 size_t* total_for_active_host) const {
385 DCHECK(client != NULL && total_delayable != NULL &&
386 total_for_active_host != NULL);
387
388 size_t total_delayable_count = 0;
389 size_t same_host_count = 0;
323 for (RequestSet::iterator it = client->in_flight_requests.begin(); 390 for (RequestSet::iterator it = client->in_flight_requests.begin();
324 it != client->in_flight_requests.end(); ++it) { 391 it != client->in_flight_requests.end(); ++it) {
392 net::HostPortPair host_port_pair =
393 net::HostPortPair::FromURL((*it)->url_request()->url());
394
395 if (active_request_host->Equals(host_port_pair)) {
396 same_host_count++;
397 }
398
325 if ((*it)->url_request()->priority() < net::LOW) { 399 if ((*it)->url_request()->priority() < net::LOW) {
326 const net::HttpServerProperties& http_server_properties = 400 const net::HttpServerProperties& http_server_properties =
327 *(*it)->url_request()->context()->http_server_properties(); 401 *(*it)->url_request()->context()->http_server_properties();
328 if (!http_server_properties.SupportsSpdy( 402
329 net::HostPortPair::FromURL((*it)->url_request()->url()))) { 403 if (!http_server_properties.SupportsSpdy(host_port_pair)) {
330 ++count; 404 ++total_delayable_count;
331 } 405 }
332 } 406 }
333 } 407 }
334 return count; 408 *total_delayable = total_delayable_count;
409 *total_for_active_host = same_host_count;
335 } 410 }
336 411
337 // ShouldStartRequest is the main scheduling algorithm. 412 // ShouldStartRequest is the main scheduling algorithm.
338 // 413 //
339 // Requests are categorized into two categories: 414 // Requests are categorized into two categories:
340 // 415 //
341 // 1. Immediately issued requests, which are: 416 // 1. Immediately issued requests, which are:
342 // 417 //
343 // * Higher priority requests (>= net::LOW). 418 // * Higher priority requests (>= net::LOW).
344 // * Synchronous requests. 419 // * Synchronous requests.
345 // * Requests to SPDY-capable origin servers. 420 // * Requests to SPDY-capable origin servers.
346 // * Non-HTTP[S] requests. 421 // * Non-HTTP[S] requests.
347 // 422 //
348 // 2. The remainder are delayable requests, which follow these rules: 423 // 2. The remainder are delayable requests, which follow these rules:
349 // 424 //
350 // * If no high priority requests are in flight, start loading low priority 425 // * If no high priority requests are in flight, start loading low priority
351 // requests. 426 // requests.
352 // * Once the renderer has a <body>, start loading delayable requests. 427 // * Once the renderer has a <body>, start loading delayable requests.
353 // * Never exceed 10 delayable requests in flight per client. 428 // * Never exceed 10 delayable requests in flight per client.
429 // * Never exceed 6 delayable requests for a given host.
354 // * Prior to <body>, allow one delayable request to load at a time. 430 // * Prior to <body>, allow one delayable request to load at a time.
355 bool ResourceScheduler::ShouldStartRequest(ScheduledResourceRequest* request, 431 ResourceScheduler::ShouldStartReqResult ResourceScheduler::ShouldStartRequest(
356 Client* client) const { 432 ScheduledResourceRequest* request,
433 Client* client) const {
357 const net::URLRequest& url_request = *request->url_request(); 434 const net::URLRequest& url_request = *request->url_request();
358 435
359 // TODO(simonjam): This may end up causing disk contention. We should 436 // TODO(simonjam): This may end up causing disk contention. We should
360 // experiment with throttling if that happens. 437 // experiment with throttling if that happens.
361 if (!url_request.url().SchemeIsHTTPOrHTTPS()) { 438 if (!url_request.url().SchemeIsHTTPOrHTTPS()) {
362 return true; 439 return START_REQUEST;
363 } 440 }
364 441
365 const net::HttpServerProperties& http_server_properties = 442 const net::HttpServerProperties& http_server_properties =
366 *url_request.context()->http_server_properties(); 443 *url_request.context()->http_server_properties();
367 444
445 if (url_request.priority() >= net::LOW ||
446 !ResourceRequestInfo::ForRequest(&url_request)->IsAsync()) {
447 return START_REQUEST;
448 }
449
450 net::HostPortPair host_port_pair =
451 net::HostPortPair::FromURL(url_request.url());
452
368 // TODO(willchan): We should really improve this algorithm as described in 453 // TODO(willchan): We should really improve this algorithm as described in
369 // crbug.com/164101. Also, theoretically we should not count a SPDY request 454 // crbug.com/164101. Also, theoretically we should not count a SPDY request
370 // against the delayable requests limit. 455 // against the delayable requests limit.
371 bool origin_supports_spdy = http_server_properties.SupportsSpdy( 456 if (http_server_properties.SupportsSpdy(host_port_pair)) {
372 net::HostPortPair::FromURL(url_request.url())); 457 return START_REQUEST;
373
374 if (url_request.priority() >= net::LOW ||
375 !ResourceRequestInfo::ForRequest(&url_request)->IsAsync() ||
376 origin_supports_spdy) {
377 return true;
378 } 458 }
379 459
380 size_t num_delayable_requests_in_flight = 460 size_t num_delayable_requests_in_flight = 0;
381 GetNumDelayableRequestsInFlight(client); 461 size_t num_requests_in_flight_for_host = 0;
462 GetNumDelayableRequestsInFlight(client, host_port_pair,
463 &num_delayable_requests_in_flight,
464 &num_requests_in_flight_for_host);
465
382 if (num_delayable_requests_in_flight >= kMaxNumDelayableRequestsPerClient) { 466 if (num_delayable_requests_in_flight >= kMaxNumDelayableRequestsPerClient) {
383 return false; 467 return DO_NOT_START_REQUEST__STOP_SEARCHING;
468 }
469
470 if (num_requests_in_flight_for_host >= kMaxNumDelayableRequestsPerHost) {
471 // There may be other requests for other hosts we'd allow, so keep checking.
472 return DO_NOT_START_REQUEST__KEEP_SEARCHING;
384 } 473 }
385 474
386 bool have_immediate_requests_in_flight = 475 bool have_immediate_requests_in_flight =
387 client->in_flight_requests.size() > num_delayable_requests_in_flight; 476 client->in_flight_requests.size() > num_delayable_requests_in_flight;
388 if (have_immediate_requests_in_flight && !client->has_body && 477 if (have_immediate_requests_in_flight && !client->has_body &&
389 num_delayable_requests_in_flight != 0) { 478 num_delayable_requests_in_flight != 0) {
390 return false; 479 return DO_NOT_START_REQUEST__STOP_SEARCHING;
391 } 480 }
392 481
393 return true; 482 return START_REQUEST;
394 } 483 }
395 484
396 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( 485 ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
397 int child_id, int route_id) { 486 int child_id, int route_id) {
398 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; 487 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id;
399 } 488 }
400 489
401 } // namespace content 490 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698