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

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: Fixed build 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) == kStartRequest) {
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 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
292 client->pending_requests.Erase(request); 330 client->pending_requests.Erase(request);
293 client->pending_requests.Insert(request, request->url_request()->priority()); 331 client->pending_requests.Insert(request, request->url_request()->priority());
294 332
295 if (new_priority > old_priority) { 333 if (new_priority > old_priority) {
296 // Check if this request is now able to load at its new priority. 334 // Check if this request is now able to load at its new priority.
297 LoadAnyStartablePendingRequests(client); 335 LoadAnyStartablePendingRequests(client);
298 } 336 }
299 } 337 }
300 338
301 void ResourceScheduler::LoadAnyStartablePendingRequests(Client* client) { 339 void ResourceScheduler::LoadAnyStartablePendingRequests(Client* client) {
302 while (!client->pending_requests.IsEmpty()) { 340 // We iterate through all the pending requests, starting with the highest
303 ScheduledResourceRequest* request = client->pending_requests.FirstMax(); 341 // priority one. For each entry, one of three things can happen:
304 if (ShouldStartRequest(request, client)) { 342 // 1) We start the request, remove it from the list, and keep checking.
343 // 2) We do NOT start the request, but ShouldStartRequest() signals us that
344 // there may be room for other requests, so we keep checking and leave
345 // the previous request still in the list.
346 // 3) We do not start the request, same as above, but StartRequest() tells
347 // us there's no point in checking any further requests.
348
349 RequestQueue::Iterator request_iter =
350 client->pending_requests.GetNextHighestIterator();
351
352 while (!request_iter.is_null()) {
353 ScheduledResourceRequest* request = request_iter.value();
354 ShouldStartReqResult query_result = ShouldStartRequest(request, client);
355
356 if (query_result == kStartRequest) {
305 client->pending_requests.Erase(request); 357 client->pending_requests.Erase(request);
306 StartRequest(request, client); 358 StartRequest(request, client);
359
360 // StartRequest can modify the pending list, so we (re)start evaluation
361 // from the currently highest priority request.
362 request_iter = client->pending_requests.GetNextHighestIterator();
363 } else if (query_result == kDoNotStartRequest_KeepSearching) {
364 ++request_iter;
365 continue;
307 } else { 366 } else {
367 DCHECK(query_result == kDoNotStartRequest_StopSearching);
James Simonsen 2013/11/13 01:26:23 I'd prefer it be a switch instead of if/else. Let
oystein (OOO til 10th of July) 2013/11/14 00:19:07 The reason I didn't go with a switch is to avoid n
308 break; 368 break;
309 } 369 }
310 } 370 }
311 } 371 }
312 372
313 size_t ResourceScheduler::GetNumDelayableRequestsInFlight( 373 void ResourceScheduler::GetNumDelayableRequestsInFlight(
James Simonsen 2013/11/13 01:26:23 It feels like we're doing a lot of work here now.
oystein (OOO til 10th of July) 2013/11/14 00:19:07 I looked into this a bit now, I think the main pro
James Simonsen 2013/11/14 19:22:43 There has to be an easier way to keep track of all
oystein (OOO til 10th of July) 2013/11/14 19:51:49 I agree, but I suggest we wait until the next chan
314 Client* client) const { 374 Client* client,
315 size_t count = 0; 375 net::HostPortPair* active_request_host,
James Simonsen 2013/11/13 01:26:23 This should be a const reference. It shouldn't eve
oystein (OOO til 10th of July) 2013/11/14 19:51:49 Done.
376 size_t* total_delayable,
377 size_t* total_for_active_host) const {
378 DCHECK(client != NULL && active_request_host != NULL &&
379 total_delayable != NULL && total_for_active_host != NULL);
380
381 size_t total_delayable_count = 0;
382 size_t same_host_count = 0;
316 for (RequestSet::iterator it = client->in_flight_requests.begin(); 383 for (RequestSet::iterator it = client->in_flight_requests.begin();
317 it != client->in_flight_requests.end(); ++it) { 384 it != client->in_flight_requests.end(); ++it) {
385 net::HostPortPair host_port_pair =
386 net::HostPortPair::FromURL((*it)->url_request()->url());
James Simonsen 2013/11/13 01:26:23 Yeah, the multimap or whatever would be nice here.
387
388 if (active_request_host->Equals(host_port_pair)) {
389 same_host_count++;
390 }
391
318 if ((*it)->url_request()->priority() < net::LOW) { 392 if ((*it)->url_request()->priority() < net::LOW) {
319 const net::HttpServerProperties& http_server_properties = 393 const net::HttpServerProperties& http_server_properties =
320 *(*it)->url_request()->context()->http_server_properties(); 394 *(*it)->url_request()->context()->http_server_properties();
321 if (!http_server_properties.SupportsSpdy( 395
322 net::HostPortPair::FromURL((*it)->url_request()->url()))) { 396 if (!http_server_properties.SupportsSpdy(host_port_pair)) {
James Simonsen 2013/11/13 01:26:23 Maybe we should keep these in a separate set.
323 ++count; 397 ++total_delayable_count;
324 } 398 }
325 } 399 }
326 } 400 }
327 return count; 401 *total_delayable = total_delayable_count;
402 *total_for_active_host = same_host_count;
328 } 403 }
329 404
330 // ShouldStartRequest is the main scheduling algorithm. 405 // ShouldStartRequest is the main scheduling algorithm.
331 // 406 //
332 // Requests are categorized into two categories: 407 // Requests are categorized into two categories:
333 // 408 //
334 // 1. Immediately issued requests, which are: 409 // 1. Immediately issued requests, which are:
335 // 410 //
336 // * Higher priority requests (>= net::LOW). 411 // * Higher priority requests (>= net::LOW).
337 // * Synchronous requests. 412 // * Synchronous requests.
338 // * Requests to SPDY-capable origin servers. 413 // * Requests to SPDY-capable origin servers.
339 // * Non-HTTP[S] requests. 414 // * Non-HTTP[S] requests.
340 // 415 //
341 // 2. The remainder are delayable requests, which follow these rules: 416 // 2. The remainder are delayable requests, which follow these rules:
342 // 417 //
343 // * If no high priority requests are in flight, start loading low priority 418 // * If no high priority requests are in flight, start loading low priority
344 // requests. 419 // requests.
345 // * Once the renderer has a <body>, start loading delayable requests. 420 // * Once the renderer has a <body>, start loading delayable requests.
346 // * Never exceed 10 delayable requests in flight per client. 421 // * Never exceed 10 delayable requests in flight per client.
422 // * Never exceed 6 delayable requests for a given host.
347 // * Prior to <body>, allow one delayable request to load at a time. 423 // * Prior to <body>, allow one delayable request to load at a time.
348 bool ResourceScheduler::ShouldStartRequest(ScheduledResourceRequest* request, 424 ResourceScheduler::ShouldStartReqResult ResourceScheduler::ShouldStartRequest(
349 Client* client) const { 425 ScheduledResourceRequest* request,
426 Client* client) const {
350 const net::URLRequest& url_request = *request->url_request(); 427 const net::URLRequest& url_request = *request->url_request();
351 428
352 // TODO(simonjam): This may end up causing disk contention. We should 429 // TODO(simonjam): This may end up causing disk contention. We should
353 // experiment with throttling if that happens. 430 // experiment with throttling if that happens.
354 if (!url_request.url().SchemeIsHTTPOrHTTPS()) { 431 if (!url_request.url().SchemeIsHTTPOrHTTPS()) {
355 return true; 432 return kStartRequest;
356 } 433 }
357 434
358 const net::HttpServerProperties& http_server_properties = 435 const net::HttpServerProperties& http_server_properties =
359 *url_request.context()->http_server_properties(); 436 *url_request.context()->http_server_properties();
360 437
438 if (url_request.priority() >= net::LOW ||
439 !ResourceRequestInfo::ForRequest(&url_request)->IsAsync()) {
440 return kStartRequest;
441 }
442
443 net::HostPortPair host_port_pair =
444 net::HostPortPair::FromURL(url_request.url());
445
361 // TODO(willchan): We should really improve this algorithm as described in 446 // TODO(willchan): We should really improve this algorithm as described in
362 // crbug.com/164101. Also, theoretically we should not count a SPDY request 447 // crbug.com/164101. Also, theoretically we should not count a SPDY request
363 // against the delayable requests limit. 448 // against the delayable requests limit.
364 bool origin_supports_spdy = http_server_properties.SupportsSpdy( 449 if (http_server_properties.SupportsSpdy(host_port_pair)) {
365 net::HostPortPair::FromURL(url_request.url())); 450 return kStartRequest;
366
367 if (url_request.priority() >= net::LOW ||
368 !ResourceRequestInfo::ForRequest(&url_request)->IsAsync() ||
369 origin_supports_spdy) {
370 return true;
371 } 451 }
372 452
373 size_t num_delayable_requests_in_flight = 453 size_t num_delayable_requests_in_flight = 0;
374 GetNumDelayableRequestsInFlight(client); 454 size_t num_requests_in_flight_for_host = 0;
455 GetNumDelayableRequestsInFlight(client, &host_port_pair,
456 &num_delayable_requests_in_flight,
457 &num_requests_in_flight_for_host);
458
375 if (num_delayable_requests_in_flight >= kMaxNumDelayableRequestsPerClient) { 459 if (num_delayable_requests_in_flight >= kMaxNumDelayableRequestsPerClient) {
376 return false; 460 return kDoNotStartRequest_StopSearching;
461 }
462
463 if (num_requests_in_flight_for_host >= kMaxNumDelayableRequestsPerHost) {
464 // There may be other requests for other hosts we'd allow, so keep checking.
465 return kDoNotStartRequest_KeepSearching;
377 } 466 }
378 467
379 bool have_immediate_requests_in_flight = 468 bool have_immediate_requests_in_flight =
380 client->in_flight_requests.size() > num_delayable_requests_in_flight; 469 client->in_flight_requests.size() > num_delayable_requests_in_flight;
381 if (have_immediate_requests_in_flight && !client->has_body && 470 if (have_immediate_requests_in_flight && !client->has_body &&
382 num_delayable_requests_in_flight != 0) { 471 num_delayable_requests_in_flight != 0) {
383 return false; 472 return kDoNotStartRequest_StopSearching;
384 } 473 }
385 474
386 return true; 475 return kStartRequest;
387 } 476 }
388 477
389 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( 478 ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
390 int child_id, int route_id) { 479 int child_id, int route_id) {
391 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; 480 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id;
392 } 481 }
393 482
394 } // namespace content 483 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698