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

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

Issue 12874003: Limit to only 10 image requests per page in ResourceScheduler. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Move inner classes to .cc Created 7 years, 9 months 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_throttle.h" 12 #include "content/public/browser/resource_throttle.h"
12 #include "ipc/ipc_message_macros.h" 13 #include "ipc/ipc_message_macros.h"
13 #include "net/base/load_flags.h" 14 #include "net/base/load_flags.h"
14 #include "net/base/request_priority.h" 15 #include "net/base/request_priority.h"
15 #include "net/url_request/url_request.h" 16 #include "net/url_request/url_request.h"
16 17
17 namespace content { 18 namespace content {
18 19
20 static const int kMaxNumLowPriorityRequestsPerClient = 10;
21
22 // Each client represents a tab.
23 struct ResourceScheduler::Client {
24 Client() : has_body(false) {}
25 ~Client() {}
26
27 bool has_body;
28 RequestQueue pending_requests;
29 RequestSet in_flight_requests;
30 };
31
32 // A thin wrapper around net::PriorityQueue that deals with
33 // ScheduledResourceRequests instead of PriorityQueue::Pointers.
34 class ResourceScheduler::RequestQueue {
35 public:
36 RequestQueue() : queue_(net::NUM_PRIORITIES) {}
37 ~RequestQueue() {}
38
39 // Adds |request| to the queue with given |priority|.
40 void Insert(ScheduledResourceRequest* request,
41 net::RequestPriority priority) {
42 DCHECK(!ContainsKey(pointers_, request));
43 NetQueue::Pointer pointer = queue_.Insert(request, priority);
44 pointers_[request] = pointer;
45 }
46
47 // Removes |request| from the queue.
48 void Erase(ScheduledResourceRequest* request) {
49 PointerMap::iterator it = pointers_.find(request);
50 DCHECK(it != pointers_.end());
51 queue_.Erase(it->second);
52 pointers_.erase(it);
53 }
54
55 // Returns the highest priority request that's queued, or NULL if none are.
56 ScheduledResourceRequest* FirstMax() {
57 return queue_.FirstMax().value();
58 }
59
60 // Returns true if |request| is queued.
61 bool IsQueued(ScheduledResourceRequest* request) const {
62 return ContainsKey(pointers_, request);
63 }
64
65 // Returns true if no requests are queued.
66 bool IsEmpty() const { return queue_.size() == 0; }
67
68 private:
69 typedef net::PriorityQueue<ScheduledResourceRequest*> NetQueue;
70 typedef std::map<ScheduledResourceRequest*, NetQueue::Pointer> PointerMap;
71
72 NetQueue queue_;
73 PointerMap pointers_;
74 };
75
76 // This is the handle we return to the ResourceDispatcherHostImpl so it can
77 // interact with the request.
19 class ResourceScheduler::ScheduledResourceRequest 78 class ResourceScheduler::ScheduledResourceRequest
20 : public ResourceMessageDelegate, 79 : public ResourceMessageDelegate,
21 public ResourceThrottle { 80 public ResourceThrottle {
22 public: 81 public:
23 ScheduledResourceRequest(const ClientId& client_id, 82 ScheduledResourceRequest(const ClientId& client_id,
24 net::URLRequest* request, 83 net::URLRequest* request,
25 ResourceScheduler* scheduler) 84 ResourceScheduler* scheduler)
26 : ResourceMessageDelegate(request), 85 : ResourceMessageDelegate(request),
27 client_id_(client_id), 86 client_id_(client_id),
28 request_(request), 87 request_(request),
29 ready_(false), 88 ready_(false),
30 deferred_(false), 89 deferred_(false),
31 scheduler_(scheduler) { 90 scheduler_(scheduler) {
32 } 91 }
33 92
34 virtual ~ScheduledResourceRequest() { 93 virtual ~ScheduledResourceRequest() {
35 scheduler_->RemoveRequest(this); 94 scheduler_->RemoveRequest(this);
36 } 95 }
37 96
38 void Start() { 97 void Start() {
39 ready_ = true; 98 ready_ = true;
40 if (deferred_ && request_->status().is_success()) { 99 if (deferred_ && request_->status().is_success()) {
41 deferred_ = false; 100 deferred_ = false;
42 controller()->Resume(); 101 controller()->Resume();
43 } 102 }
44 } 103 }
45 104
46 const ClientId& client_id() const { return client_id_; } 105 const ClientId& client_id() const { return client_id_; }
47 const net::URLRequest& url_request() const { return *request_; } 106 net::URLRequest* url_request() { return request_; }
107 const net::URLRequest* url_request() const { return request_; }
48 108
49 private: 109 private:
50 // ResourceMessageDelegate interface: 110 // ResourceMessageDelegate interface:
51 virtual bool OnMessageReceived(const IPC::Message& message, 111 virtual bool OnMessageReceived(const IPC::Message& message,
52 bool* message_was_ok) OVERRIDE { 112 bool* message_was_ok) OVERRIDE {
53 bool handled = true; 113 bool handled = true;
54 IPC_BEGIN_MESSAGE_MAP_EX(ScheduledResourceRequest, message, *message_was_ok) 114 IPC_BEGIN_MESSAGE_MAP_EX(ScheduledResourceRequest, message, *message_was_ok)
55 IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority) 115 IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority)
56 IPC_MESSAGE_UNHANDLED(handled = false) 116 IPC_MESSAGE_UNHANDLED(handled = false)
57 IPC_END_MESSAGE_MAP_EX() 117 IPC_END_MESSAGE_MAP_EX()
58 return handled; 118 return handled;
59 } 119 }
60 120
61 // ResourceThrottle interface: 121 // ResourceThrottle interface:
62 virtual void WillStartRequest(bool* defer) OVERRIDE { 122 virtual void WillStartRequest(bool* defer) OVERRIDE {
63 deferred_ = *defer = !ready_; 123 deferred_ = *defer = !ready_;
64 } 124 }
65 125
66 void DidChangePriority(int request_id, net::RequestPriority new_priority) { 126 void DidChangePriority(int request_id, net::RequestPriority new_priority) {
67 net::RequestPriority old_priority = request_->priority(); 127 scheduler_->ReprioritizeRequest(this, new_priority);
68 request_->set_priority(new_priority);
69 if (new_priority > old_priority) {
70 Start();
71 }
72 } 128 }
73 129
74 ClientId client_id_; 130 ClientId client_id_;
75 net::URLRequest* request_; 131 net::URLRequest* request_;
76 bool ready_; 132 bool ready_;
77 bool deferred_; 133 bool deferred_;
78 ResourceScheduler* scheduler_; 134 ResourceScheduler* scheduler_;
79 135
80 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest); 136 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest);
81 }; 137 };
82 138
83 ResourceScheduler::ResourceScheduler() { 139 ResourceScheduler::ResourceScheduler() {
84 } 140 }
85 141
86 ResourceScheduler::~ResourceScheduler() { 142 ResourceScheduler::~ResourceScheduler() {
87 for (ClientMap::iterator it ALLOW_UNUSED = client_map_.begin(); 143 for (ClientMap::iterator it ALLOW_UNUSED = client_map_.begin();
willchan no longer on Chromium 2013/03/19 20:53:53 Sorry again for nitting on existing code. Why does
James Simonsen 2013/03/19 23:00:46 Done. It used to be more useful.
88 it != client_map_.end(); ++it) { 144 it != client_map_.end(); ++it) {
89 DCHECK(it->second->pending_requests.empty()); 145 DCHECK(it->second->pending_requests.IsEmpty());
90 DCHECK(it->second->in_flight_requests.empty()); 146 DCHECK(it->second->in_flight_requests.empty());
91 } 147 }
92 DCHECK(unowned_requests_.empty()); 148 DCHECK(unowned_requests_.empty());
93 DCHECK(client_map_.empty()); 149 DCHECK(client_map_.empty());
94 } 150 }
95 151
96 scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( 152 scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest(
97 int child_id, 153 int child_id,
98 int route_id, 154 int route_id,
99 net::URLRequest* url_request) { 155 net::URLRequest* url_request) {
100 DCHECK(CalledOnValidThread()); 156 DCHECK(CalledOnValidThread());
101 ClientId client_id = MakeClientId(child_id, route_id); 157 ClientId client_id = MakeClientId(child_id, route_id);
102 scoped_ptr<ScheduledResourceRequest> request( 158 scoped_ptr<ScheduledResourceRequest> request(
103 new ScheduledResourceRequest(client_id, url_request, this)); 159 new ScheduledResourceRequest(client_id, url_request, this));
104 160
105 ClientMap::iterator it = client_map_.find(client_id); 161 ClientMap::iterator it = client_map_.find(client_id);
106 if (it == client_map_.end()) { 162 if (it == client_map_.end()) {
107 // There are several ways this could happen: 163 // There are several ways this could happen:
108 // 1. <a ping> requests don't have a route_id. 164 // 1. <a ping> requests don't have a route_id.
109 // 2. Most unittests don't send the IPCs needed to register Clients. 165 // 2. Most unittests don't send the IPCs needed to register Clients.
110 // 3. The tab is closed while a RequestResource IPC is in flight. 166 // 3. The tab is closed while a RequestResource IPC is in flight.
111 unowned_requests_.insert(request.get()); 167 unowned_requests_.insert(request.get());
112 request->Start(); 168 request->Start();
113 return request.PassAs<ResourceThrottle>(); 169 return request.PassAs<ResourceThrottle>();
114 } 170 }
115 171
116 Client* client = it->second; 172 Client* client = it->second;
117 173 if (ShouldStartRequest(request.get(), client)) {
118 bool is_synchronous = (url_request->load_flags() & net::LOAD_IGNORE_LIMITS) == 174 StartRequest(request.get(), client);
119 net::LOAD_IGNORE_LIMITS;
120 bool is_low_priority =
121 url_request->priority() < net::LOW && !is_synchronous;
122
123 if (is_low_priority && !client->in_flight_requests.empty() &&
124 !client->has_body) {
125 client->pending_requests.push_back(request.get());
126 } else { 175 } else {
127 StartRequest(request.get(), client); 176 client->pending_requests.Insert(request.get(), url_request->priority());
128 } 177 }
129 return request.PassAs<ResourceThrottle>(); 178 return request.PassAs<ResourceThrottle>();
130 } 179 }
131 180
132 void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) { 181 void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) {
133 DCHECK(CalledOnValidThread()); 182 DCHECK(CalledOnValidThread());
134 if (ContainsKey(unowned_requests_, request)) { 183 if (ContainsKey(unowned_requests_, request)) {
135 unowned_requests_.erase(request); 184 unowned_requests_.erase(request);
136 return; 185 return;
137 } 186 }
138 187
139 ClientMap::iterator client_it = client_map_.find(request->client_id()); 188 ClientMap::iterator client_it = client_map_.find(request->client_id());
140 if (client_it == client_map_.end()) { 189 if (client_it == client_map_.end()) {
141 return; 190 return;
142 } 191 }
143 192
144 Client* client = client_it->second; 193 Client* client = client_it->second;
145 RequestSet::iterator request_it = client->in_flight_requests.find(request); 194
146 if (request_it == client->in_flight_requests.end()) { 195 if (client->pending_requests.IsQueued(request)) {
147 bool removed = false; 196 client->pending_requests.Erase(request);
148 RequestQueue::iterator queue_it;
149 for (queue_it = client->pending_requests.begin();
150 queue_it != client->pending_requests.end(); ++queue_it) {
151 if (*queue_it == request) {
152 client->pending_requests.erase(queue_it);
153 removed = true;
154 break;
155 }
156 }
157 DCHECK(removed);
158 DCHECK(!ContainsKey(client->in_flight_requests, request)); 197 DCHECK(!ContainsKey(client->in_flight_requests, request));
159 } else { 198 } else {
160 size_t erased = client->in_flight_requests.erase(request); 199 size_t erased = client->in_flight_requests.erase(request);
161 DCHECK(erased); 200 DCHECK(erased);
162 }
163 201
164 if (client->in_flight_requests.empty()) { 202 // Removing this request may have freed up another to load.
165 // Since the network is now idle, we may as well load some of the low 203 LoadAnyStartablePendingRequests(client);
166 // priority requests.
167 LoadPendingRequests(client);
168 } 204 }
169 } 205 }
170 206
171 void ResourceScheduler::OnClientCreated(int child_id, int route_id) { 207 void ResourceScheduler::OnClientCreated(int child_id, int route_id) {
172 DCHECK(CalledOnValidThread()); 208 DCHECK(CalledOnValidThread());
173 ClientId client_id = MakeClientId(child_id, route_id); 209 ClientId client_id = MakeClientId(child_id, route_id);
174 DCHECK(!ContainsKey(client_map_, client_id)); 210 DCHECK(!ContainsKey(client_map_, client_id));
175 211
176 client_map_[client_id] = new Client; 212 client_map_[client_id] = new Client;
177 } 213 }
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
217 ClientMap::iterator it = client_map_.find(client_id); 253 ClientMap::iterator it = client_map_.find(client_id);
218 if (it == client_map_.end()) { 254 if (it == client_map_.end()) {
219 // The client was likely deleted shortly before we received this IPC. 255 // The client was likely deleted shortly before we received this IPC.
220 return; 256 return;
221 } 257 }
222 258
223 Client* client = it->second; 259 Client* client = it->second;
224 client->has_body = false; 260 client->has_body = false;
225 if (!client->has_body) { 261 if (!client->has_body) {
226 client->has_body = true; 262 client->has_body = true;
227 LoadPendingRequests(client); 263 LoadAnyStartablePendingRequests(client);
228 } 264 }
229 } 265 }
230 266
231 void ResourceScheduler::StartRequest(ScheduledResourceRequest* request, 267 void ResourceScheduler::StartRequest(ScheduledResourceRequest* request,
232 Client* client) { 268 Client* client) {
233 client->in_flight_requests.insert(request); 269 client->in_flight_requests.insert(request);
234 request->Start(); 270 request->Start();
235 } 271 }
236 272
237 void ResourceScheduler::LoadPendingRequests(Client* client) { 273 void ResourceScheduler::ReprioritizeRequest(ScheduledResourceRequest* request,
238 while (!client->pending_requests.empty()) { 274 net::RequestPriority new_priority) {
239 ScheduledResourceRequest* request = client->pending_requests.front(); 275 net::RequestPriority old_priority = request->url_request()->priority();
240 client->pending_requests.erase(client->pending_requests.begin()); 276 DCHECK(new_priority != old_priority);
willchan no longer on Chromium 2013/03/19 20:53:53 Does DCHECK_NE() not work?
James Simonsen 2013/03/19 23:00:46 Done.
241 StartRequest(request, client); 277 request->url_request()->set_priority(new_priority);
278 ClientMap::iterator client_it = client_map_.find(request->client_id());
279 if (client_it == client_map_.end()) {
280 // The client was likely deleted shortly before we received this IPC.
willchan no longer on Chromium 2013/03/19 20:53:53 I think this RenderViewHost deletion signal seems
James Simonsen 2013/03/19 23:00:46 There can be several renderers on the same channel
willchan no longer on Chromium 2013/03/19 23:08:32 Sorry, forgot about that. But why don't we get a R
281 return;
242 } 282 }
283
284 Client *client = client_it->second;
285 if (!client->pending_requests.IsQueued(request)) {
286 DCHECK(ContainsKey(client->in_flight_requests, request));
287 // Request has already started.
288 return;
289 }
290
291 client->pending_requests.Erase(request);
292 client->pending_requests.Insert(request, request->url_request()->priority());
293
294 if (new_priority > old_priority) {
295 // Check if this request is now able to load at its new priority.
296 LoadAnyStartablePendingRequests(client);
297 }
298 }
299
300 void ResourceScheduler::LoadAnyStartablePendingRequests(Client* client) {
301 while (!client->pending_requests.IsEmpty()) {
302 ScheduledResourceRequest* request = client->pending_requests.FirstMax();
303 if (ShouldStartRequest(request, client)) {
304 client->pending_requests.Erase(request);
305 StartRequest(request, client);
306 } else {
307 break;
308 }
309 }
310 }
311
312 size_t ResourceScheduler::GetNumLowPriorityRequestsInFlight(
313 Client* client) const {
314 size_t count = 0;
315 for (RequestSet::iterator it = client->in_flight_requests.begin();
316 it != client->in_flight_requests.end(); ++it) {
317 if ((*it)->url_request()->priority() < net::LOW) {
318 ++count;
319 }
320 }
321 return count;
322 }
323
324 // ShouldStartRequest is the main scheduling algorithm. It works as follows:
325 //
326 // 1. High priority requests (>= net::LOW are always issued.
willchan no longer on Chromium 2013/03/19 20:53:53 This is very unfortunately named. Can we fix this
James Simonsen 2013/03/19 23:00:46 The whole notion of using priority names is ridicu
327 // 2. Synchronous requests are always high priority.
willchan no longer on Chromium 2013/03/19 20:53:53 I'd say instead that synchronous requests are alwa
James Simonsen 2013/03/19 23:00:46 Done.
328 //
329 // The remaining, low priority follow these rules:
330 //
331 // 1. If no high priority requests are in flight, start loading low priority
332 // requests.
333 // 2. Once the renderer has a <body>, start loading low priority requests.
334 // 3. Never exceed 10 low priority requests in flight per client.
335 bool ResourceScheduler::ShouldStartRequest(ScheduledResourceRequest* request,
336 Client* client) const {
337 if (request->url_request()->priority() >= net::LOW ||
338 !ResourceRequestInfo::ForRequest(request->url_request())->IsAsync()) {
339 return true;
340 }
341
342 size_t num_low_priority_requests_in_flight =
343 GetNumLowPriorityRequestsInFlight(client);
344 if (num_low_priority_requests_in_flight >=
345 kMaxNumLowPriorityRequestsPerClient) {
346 return false;
347 }
348
349 bool have_high_priority_requests_in_flight =
350 client->in_flight_requests.size() > num_low_priority_requests_in_flight;
351 if (have_high_priority_requests_in_flight && !client->has_body) {
352 return false;
353 }
354
355 return true;
243 } 356 }
244 357
245 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( 358 ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
246 int child_id, int route_id) { 359 int child_id, int route_id) {
247 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; 360 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id;
248 } 361 }
249 362
250 ResourceScheduler::Client::Client()
251 : has_body(false) {
252 }
253
254 ResourceScheduler::Client::~Client() {
255 }
256
257 } // namespace content 363 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698