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 "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 Loading... |
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 Loading... |
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 |
OLD | NEW |