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

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: Rebase 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 size_t kMaxNumDelayableRequestsPerClient = 10;
21
22 // A thin wrapper around net::PriorityQueue that deals with
23 // ScheduledResourceRequests instead of PriorityQueue::Pointers.
24 class ResourceScheduler::RequestQueue {
25 public:
26 RequestQueue() : queue_(net::NUM_PRIORITIES) {}
27 ~RequestQueue() {}
28
29 // Adds |request| to the queue with given |priority|.
30 void Insert(ScheduledResourceRequest* request,
31 net::RequestPriority priority) {
32 DCHECK(!ContainsKey(pointers_, request));
33 NetQueue::Pointer pointer = queue_.Insert(request, priority);
34 pointers_[request] = pointer;
35 }
36
37 // Removes |request| from the queue.
38 void Erase(ScheduledResourceRequest* request) {
39 PointerMap::iterator it = pointers_.find(request);
40 DCHECK(it != pointers_.end());
41 queue_.Erase(it->second);
42 pointers_.erase(it);
43 }
44
45 // Returns the highest priority request that's queued, or NULL if none are.
46 ScheduledResourceRequest* FirstMax() {
47 return queue_.FirstMax().value();
48 }
49
50 // Returns true if |request| is queued.
51 bool IsQueued(ScheduledResourceRequest* request) const {
52 return ContainsKey(pointers_, request);
53 }
54
55 // Returns true if no requests are queued.
56 bool IsEmpty() const { return queue_.size() == 0; }
57
58 private:
59 typedef net::PriorityQueue<ScheduledResourceRequest*> NetQueue;
60 typedef std::map<ScheduledResourceRequest*, NetQueue::Pointer> PointerMap;
61
62 NetQueue queue_;
63 PointerMap pointers_;
64 };
65
66 // This is the handle we return to the ResourceDispatcherHostImpl so it can
67 // interact with the request.
19 class ResourceScheduler::ScheduledResourceRequest 68 class ResourceScheduler::ScheduledResourceRequest
20 : public ResourceMessageDelegate, 69 : public ResourceMessageDelegate,
21 public ResourceThrottle { 70 public ResourceThrottle {
22 public: 71 public:
23 ScheduledResourceRequest(const ClientId& client_id, 72 ScheduledResourceRequest(const ClientId& client_id,
24 net::URLRequest* request, 73 net::URLRequest* request,
25 ResourceScheduler* scheduler) 74 ResourceScheduler* scheduler)
26 : ResourceMessageDelegate(request), 75 : ResourceMessageDelegate(request),
27 client_id_(client_id), 76 client_id_(client_id),
28 request_(request), 77 request_(request),
29 ready_(false), 78 ready_(false),
30 deferred_(false), 79 deferred_(false),
31 scheduler_(scheduler) { 80 scheduler_(scheduler) {
32 } 81 }
33 82
34 virtual ~ScheduledResourceRequest() { 83 virtual ~ScheduledResourceRequest() {
35 scheduler_->RemoveRequest(this); 84 scheduler_->RemoveRequest(this);
36 } 85 }
37 86
38 void Start() { 87 void Start() {
39 ready_ = true; 88 ready_ = true;
40 if (deferred_ && request_->status().is_success()) { 89 if (deferred_ && request_->status().is_success()) {
41 deferred_ = false; 90 deferred_ = false;
42 controller()->Resume(); 91 controller()->Resume();
43 } 92 }
44 } 93 }
45 94
46 const ClientId& client_id() const { return client_id_; } 95 const ClientId& client_id() const { return client_id_; }
47 const net::URLRequest& url_request() const { return *request_; } 96 net::URLRequest* url_request() { return request_; }
97 const net::URLRequest* url_request() const { return request_; }
48 98
49 private: 99 private:
50 // ResourceMessageDelegate interface: 100 // ResourceMessageDelegate interface:
51 virtual bool OnMessageReceived(const IPC::Message& message, 101 virtual bool OnMessageReceived(const IPC::Message& message,
52 bool* message_was_ok) OVERRIDE { 102 bool* message_was_ok) OVERRIDE {
53 bool handled = true; 103 bool handled = true;
54 IPC_BEGIN_MESSAGE_MAP_EX(ScheduledResourceRequest, message, *message_was_ok) 104 IPC_BEGIN_MESSAGE_MAP_EX(ScheduledResourceRequest, message, *message_was_ok)
55 IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority) 105 IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority)
56 IPC_MESSAGE_UNHANDLED(handled = false) 106 IPC_MESSAGE_UNHANDLED(handled = false)
57 IPC_END_MESSAGE_MAP_EX() 107 IPC_END_MESSAGE_MAP_EX()
58 return handled; 108 return handled;
59 } 109 }
60 110
61 // ResourceThrottle interface: 111 // ResourceThrottle interface:
62 virtual void WillStartRequest(bool* defer) OVERRIDE { 112 virtual void WillStartRequest(bool* defer) OVERRIDE {
63 deferred_ = *defer = !ready_; 113 deferred_ = *defer = !ready_;
64 } 114 }
65 115
66 void DidChangePriority(int request_id, net::RequestPriority new_priority) { 116 void DidChangePriority(int request_id, net::RequestPriority new_priority) {
67 net::RequestPriority old_priority = request_->priority(); 117 scheduler_->ReprioritizeRequest(this, new_priority);
68 request_->SetPriority(new_priority);
69 if (new_priority > old_priority) {
70 Start();
71 }
72 } 118 }
73 119
74 ClientId client_id_; 120 ClientId client_id_;
75 net::URLRequest* request_; 121 net::URLRequest* request_;
76 bool ready_; 122 bool ready_;
77 bool deferred_; 123 bool deferred_;
78 ResourceScheduler* scheduler_; 124 ResourceScheduler* scheduler_;
79 125
80 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest); 126 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest);
81 }; 127 };
82 128
129 // Each client represents a tab.
130 struct ResourceScheduler::Client {
131 Client() : has_body(false) {}
132 ~Client() {}
133
134 bool has_body;
135 RequestQueue pending_requests;
136 RequestSet in_flight_requests;
137 };
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();
88 it != client_map_.end(); ++it) {
89 DCHECK(it->second->pending_requests.empty());
90 DCHECK(it->second->in_flight_requests.empty());
91 }
92 DCHECK(unowned_requests_.empty()); 143 DCHECK(unowned_requests_.empty());
93 DCHECK(client_map_.empty()); 144 DCHECK(client_map_.empty());
94 } 145 }
95 146
96 scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( 147 scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest(
97 int child_id, 148 int child_id,
98 int route_id, 149 int route_id,
99 net::URLRequest* url_request) { 150 net::URLRequest* url_request) {
100 DCHECK(CalledOnValidThread()); 151 DCHECK(CalledOnValidThread());
101 ClientId client_id = MakeClientId(child_id, route_id); 152 ClientId client_id = MakeClientId(child_id, route_id);
102 scoped_ptr<ScheduledResourceRequest> request( 153 scoped_ptr<ScheduledResourceRequest> request(
103 new ScheduledResourceRequest(client_id, url_request, this)); 154 new ScheduledResourceRequest(client_id, url_request, this));
104 155
105 ClientMap::iterator it = client_map_.find(client_id); 156 ClientMap::iterator it = client_map_.find(client_id);
106 if (it == client_map_.end()) { 157 if (it == client_map_.end()) {
107 // There are several ways this could happen: 158 // There are several ways this could happen:
108 // 1. <a ping> requests don't have a route_id. 159 // 1. <a ping> requests don't have a route_id.
109 // 2. Most unittests don't send the IPCs needed to register Clients. 160 // 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. 161 // 3. The tab is closed while a RequestResource IPC is in flight.
111 unowned_requests_.insert(request.get()); 162 unowned_requests_.insert(request.get());
112 request->Start(); 163 request->Start();
113 return request.PassAs<ResourceThrottle>(); 164 return request.PassAs<ResourceThrottle>();
114 } 165 }
115 166
116 Client* client = it->second; 167 Client* client = it->second;
117 168 if (ShouldStartRequest(request.get(), client)) {
118 bool is_synchronous = (url_request->load_flags() & net::LOAD_IGNORE_LIMITS) == 169 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 { 170 } else {
127 StartRequest(request.get(), client); 171 client->pending_requests.Insert(request.get(), url_request->priority());
128 } 172 }
129 return request.PassAs<ResourceThrottle>(); 173 return request.PassAs<ResourceThrottle>();
130 } 174 }
131 175
132 void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) { 176 void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) {
133 DCHECK(CalledOnValidThread()); 177 DCHECK(CalledOnValidThread());
134 if (ContainsKey(unowned_requests_, request)) { 178 if (ContainsKey(unowned_requests_, request)) {
135 unowned_requests_.erase(request); 179 unowned_requests_.erase(request);
136 return; 180 return;
137 } 181 }
138 182
139 ClientMap::iterator client_it = client_map_.find(request->client_id()); 183 ClientMap::iterator client_it = client_map_.find(request->client_id());
140 if (client_it == client_map_.end()) { 184 if (client_it == client_map_.end()) {
141 return; 185 return;
142 } 186 }
143 187
144 Client* client = client_it->second; 188 Client* client = client_it->second;
145 RequestSet::iterator request_it = client->in_flight_requests.find(request); 189
146 if (request_it == client->in_flight_requests.end()) { 190 if (client->pending_requests.IsQueued(request)) {
147 bool removed = false; 191 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)); 192 DCHECK(!ContainsKey(client->in_flight_requests, request));
159 } else { 193 } else {
160 size_t erased = client->in_flight_requests.erase(request); 194 size_t erased = client->in_flight_requests.erase(request);
161 DCHECK(erased); 195 DCHECK(erased);
162 }
163 196
164 if (client->in_flight_requests.empty()) { 197 // 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 198 LoadAnyStartablePendingRequests(client);
166 // priority requests.
167 LoadPendingRequests(client);
168 } 199 }
169 } 200 }
170 201
171 void ResourceScheduler::OnClientCreated(int child_id, int route_id) { 202 void ResourceScheduler::OnClientCreated(int child_id, int route_id) {
172 DCHECK(CalledOnValidThread()); 203 DCHECK(CalledOnValidThread());
173 ClientId client_id = MakeClientId(child_id, route_id); 204 ClientId client_id = MakeClientId(child_id, route_id);
174 DCHECK(!ContainsKey(client_map_, client_id)); 205 DCHECK(!ContainsKey(client_map_, client_id));
175 206
176 client_map_[client_id] = new Client; 207 client_map_[client_id] = new Client;
177 } 208 }
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
217 ClientMap::iterator it = client_map_.find(client_id); 248 ClientMap::iterator it = client_map_.find(client_id);
218 if (it == client_map_.end()) { 249 if (it == client_map_.end()) {
219 // The client was likely deleted shortly before we received this IPC. 250 // The client was likely deleted shortly before we received this IPC.
220 return; 251 return;
221 } 252 }
222 253
223 Client* client = it->second; 254 Client* client = it->second;
224 client->has_body = false; 255 client->has_body = false;
225 if (!client->has_body) { 256 if (!client->has_body) {
226 client->has_body = true; 257 client->has_body = true;
227 LoadPendingRequests(client); 258 LoadAnyStartablePendingRequests(client);
228 } 259 }
229 } 260 }
230 261
231 void ResourceScheduler::StartRequest(ScheduledResourceRequest* request, 262 void ResourceScheduler::StartRequest(ScheduledResourceRequest* request,
232 Client* client) { 263 Client* client) {
233 client->in_flight_requests.insert(request); 264 client->in_flight_requests.insert(request);
234 request->Start(); 265 request->Start();
235 } 266 }
236 267
237 void ResourceScheduler::LoadPendingRequests(Client* client) { 268 void ResourceScheduler::ReprioritizeRequest(ScheduledResourceRequest* request,
238 while (!client->pending_requests.empty()) { 269 net::RequestPriority new_priority) {
239 ScheduledResourceRequest* request = client->pending_requests.front(); 270 net::RequestPriority old_priority = request->url_request()->priority();
240 client->pending_requests.erase(client->pending_requests.begin()); 271 DCHECK_NE(new_priority, old_priority);
241 StartRequest(request, client); 272 request->url_request()->SetPriority(new_priority);
273 ClientMap::iterator client_it = client_map_.find(request->client_id());
274 if (client_it == client_map_.end()) {
275 // The client was likely deleted shortly before we received this IPC.
276 return;
242 } 277 }
278
279 Client *client = client_it->second;
280 if (!client->pending_requests.IsQueued(request)) {
281 DCHECK(ContainsKey(client->in_flight_requests, request));
282 // Request has already started.
283 return;
284 }
285
286 client->pending_requests.Erase(request);
287 client->pending_requests.Insert(request, request->url_request()->priority());
288
289 if (new_priority > old_priority) {
290 // Check if this request is now able to load at its new priority.
291 LoadAnyStartablePendingRequests(client);
292 }
293 }
294
295 void ResourceScheduler::LoadAnyStartablePendingRequests(Client* client) {
296 while (!client->pending_requests.IsEmpty()) {
297 ScheduledResourceRequest* request = client->pending_requests.FirstMax();
298 if (ShouldStartRequest(request, client)) {
299 client->pending_requests.Erase(request);
300 StartRequest(request, client);
301 } else {
302 break;
303 }
304 }
305 }
306
307 size_t ResourceScheduler::GetNumDelayableRequestsInFlight(
308 Client* client) const {
309 size_t count = 0;
310 for (RequestSet::iterator it = client->in_flight_requests.begin();
311 it != client->in_flight_requests.end(); ++it) {
312 if ((*it)->url_request()->priority() < net::LOW) {
313 ++count;
314 }
315 }
316 return count;
317 }
318
319 // ShouldStartRequest is the main scheduling algorithm.
320 //
321 // Requests are categorized into two categories:
322 //
323 // 1. Immediately issued requests, which are:
324 //
325 // * Higher priority requests (>= net::LOW).
326 // * Synchronous requests.
327 //
328 // 2. The remainder are delayable requests, which follow these rules:
329 //
330 // * If no high priority requests are in flight, start loading low priority
331 // requests.
332 // * Once the renderer has a <body>, start loading delayable requests.
333 // * Never exceed 10 delayable requests in flight per client.
334 bool ResourceScheduler::ShouldStartRequest(ScheduledResourceRequest* request,
335 Client* client) const {
336 if (request->url_request()->priority() >= net::LOW ||
337 !ResourceRequestInfo::ForRequest(request->url_request())->IsAsync()) {
338 return true;
339 }
340
341 size_t num_delayable_requests_in_flight =
342 GetNumDelayableRequestsInFlight(client);
343 if (num_delayable_requests_in_flight >= kMaxNumDelayableRequestsPerClient) {
344 return false;
345 }
346
347 bool have_immediate_requests_in_flight =
348 client->in_flight_requests.size() > num_delayable_requests_in_flight;
349 if (have_immediate_requests_in_flight && !client->has_body) {
350 return false;
351 }
352
353 return true;
243 } 354 }
244 355
245 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( 356 ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
246 int child_id, int route_id) { 357 int child_id, int route_id) {
247 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; 358 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id;
248 } 359 }
249 360
250 ResourceScheduler::Client::Client()
251 : has_body(false) {
252 }
253
254 ResourceScheduler::Client::~Client() {
255 }
256
257 } // namespace content 361 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/loader/resource_scheduler.h ('k') | content/browser/loader/resource_scheduler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698