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

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: 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_throttle.h" 11 #include "content/public/browser/resource_throttle.h"
12 #include "ipc/ipc_message_macros.h" 12 #include "ipc/ipc_message_macros.h"
13 #include "net/base/load_flags.h" 13 #include "net/base/load_flags.h"
14 #include "net/base/request_priority.h" 14 #include "net/base/request_priority.h"
15 #include "net/url_request/url_request.h" 15 #include "net/url_request/url_request.h"
16 16
17 namespace content { 17 namespace content {
18 18
19 static const int kMaxNumLowPriorityRequestsPerClient = 10;
20
19 class ResourceScheduler::ScheduledResourceRequest 21 class ResourceScheduler::ScheduledResourceRequest
20 : public ResourceMessageDelegate, 22 : public ResourceMessageDelegate,
21 public ResourceThrottle { 23 public ResourceThrottle {
22 public: 24 public:
23 ScheduledResourceRequest(const ClientId& client_id, 25 ScheduledResourceRequest(const ClientId& client_id,
24 net::URLRequest* request, 26 net::URLRequest* request,
25 ResourceScheduler* scheduler) 27 ResourceScheduler* scheduler)
26 : ResourceMessageDelegate(request), 28 : ResourceMessageDelegate(request),
27 client_id_(client_id), 29 client_id_(client_id),
28 request_(request), 30 request_(request),
29 ready_(false), 31 ready_(false),
30 deferred_(false), 32 deferred_(false),
31 scheduler_(scheduler) { 33 scheduler_(scheduler) {
32 } 34 }
33 35
34 virtual ~ScheduledResourceRequest() { 36 virtual ~ScheduledResourceRequest() {
35 scheduler_->RemoveRequest(this); 37 scheduler_->RemoveRequest(this);
36 } 38 }
37 39
38 void Start() { 40 void Start() {
39 ready_ = true; 41 ready_ = true;
40 if (deferred_ && request_->status().is_success()) { 42 if (deferred_ && request_->status().is_success()) {
41 deferred_ = false; 43 deferred_ = false;
42 controller()->Resume(); 44 controller()->Resume();
43 } 45 }
44 } 46 }
45 47
48 // |queue_pointer| keeps track of the position of |this| in its client's
49 // |pending_requests| queue.
50 ResourceScheduler::RequestQueue::Pointer queue_pointer() {
51 return queue_pointer_;
52 }
53 void set_queue_pointer(ResourceScheduler::RequestQueue::Pointer pointer) {
54 queue_pointer_ = pointer;
55 }
56 void clear_queue_pointer() {
57 queue_pointer_.Reset();
58 }
59
46 const ClientId& client_id() const { return client_id_; } 60 const ClientId& client_id() const { return client_id_; }
47 const net::URLRequest& url_request() const { return *request_; } 61 const net::URLRequest& url_request() const { return *request_; }
48 62
49 private: 63 private:
50 // ResourceMessageDelegate interface: 64 // ResourceMessageDelegate interface:
51 virtual bool OnMessageReceived(const IPC::Message& message, 65 virtual bool OnMessageReceived(const IPC::Message& message,
52 bool* message_was_ok) OVERRIDE { 66 bool* message_was_ok) OVERRIDE {
53 bool handled = true; 67 bool handled = true;
54 IPC_BEGIN_MESSAGE_MAP_EX(ScheduledResourceRequest, message, *message_was_ok) 68 IPC_BEGIN_MESSAGE_MAP_EX(ScheduledResourceRequest, message, *message_was_ok)
55 IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority) 69 IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority)
56 IPC_MESSAGE_UNHANDLED(handled = false) 70 IPC_MESSAGE_UNHANDLED(handled = false)
57 IPC_END_MESSAGE_MAP_EX() 71 IPC_END_MESSAGE_MAP_EX()
58 return handled; 72 return handled;
59 } 73 }
60 74
61 // ResourceThrottle interface: 75 // ResourceThrottle interface:
62 virtual void WillStartRequest(bool* defer) OVERRIDE { 76 virtual void WillStartRequest(bool* defer) OVERRIDE {
63 deferred_ = *defer = !ready_; 77 deferred_ = *defer = !ready_;
64 } 78 }
65 79
66 void DidChangePriority(int request_id, net::RequestPriority new_priority) { 80 void DidChangePriority(int request_id, net::RequestPriority new_priority) {
67 net::RequestPriority old_priority = request_->priority(); 81 net::RequestPriority old_priority = request_->priority();
68 request_->set_priority(new_priority); 82 if (new_priority != old_priority) {
willchan no longer on Chromium 2013/03/18 07:30:33 When is this ever false? Sounds like a renderer bu
James Simonsen 2013/03/18 23:50:13 Agreed. Done.
69 if (new_priority > old_priority) { 83 request_->set_priority(new_priority);
70 Start(); 84 scheduler_->ReprioritizeRequest(this);
71 } 85 }
72 } 86 }
73 87
74 ClientId client_id_; 88 ClientId client_id_;
75 net::URLRequest* request_; 89 net::URLRequest* request_;
76 bool ready_; 90 bool ready_;
77 bool deferred_; 91 bool deferred_;
78 ResourceScheduler* scheduler_; 92 ResourceScheduler* scheduler_;
93 ResourceScheduler::RequestQueue::Pointer queue_pointer_;
79 94
80 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest); 95 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest);
81 }; 96 };
82 97
83 ResourceScheduler::ResourceScheduler() { 98 ResourceScheduler::ResourceScheduler() {
84 } 99 }
85 100
86 ResourceScheduler::~ResourceScheduler() { 101 ResourceScheduler::~ResourceScheduler() {
87 for (ClientMap::iterator it ALLOW_UNUSED = client_map_.begin(); 102 for (ClientMap::iterator it ALLOW_UNUSED = client_map_.begin();
88 it != client_map_.end(); ++it) { 103 it != client_map_.end(); ++it) {
89 DCHECK(it->second->pending_requests.empty()); 104 DCHECK(!it->second->pending_requests.size());
90 DCHECK(it->second->in_flight_requests.empty()); 105 DCHECK(it->second->in_flight_requests.empty());
91 } 106 }
92 DCHECK(unowned_requests_.empty()); 107 DCHECK(unowned_requests_.empty());
93 DCHECK(client_map_.empty()); 108 DCHECK(client_map_.empty());
94 } 109 }
95 110
96 scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( 111 scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest(
97 int child_id, 112 int child_id,
98 int route_id, 113 int route_id,
99 net::URLRequest* url_request) { 114 net::URLRequest* url_request) {
100 DCHECK(CalledOnValidThread()); 115 DCHECK(CalledOnValidThread());
101 ClientId client_id = MakeClientId(child_id, route_id); 116 ClientId client_id = MakeClientId(child_id, route_id);
102 scoped_ptr<ScheduledResourceRequest> request( 117 scoped_ptr<ScheduledResourceRequest> request(
103 new ScheduledResourceRequest(client_id, url_request, this)); 118 new ScheduledResourceRequest(client_id, url_request, this));
104 119
105 ClientMap::iterator it = client_map_.find(client_id); 120 ClientMap::iterator it = client_map_.find(client_id);
106 if (it == client_map_.end()) { 121 if (it == client_map_.end()) {
107 // There are several ways this could happen: 122 // There are several ways this could happen:
108 // 1. <a ping> requests don't have a route_id. 123 // 1. <a ping> requests don't have a route_id.
109 // 2. Most unittests don't send the IPCs needed to register Clients. 124 // 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. 125 // 3. The tab is closed while a RequestResource IPC is in flight.
111 unowned_requests_.insert(request.get()); 126 unowned_requests_.insert(request.get());
112 request->Start(); 127 request->Start();
113 return request.PassAs<ResourceThrottle>(); 128 return request.PassAs<ResourceThrottle>();
114 } 129 }
115 130
116 Client* client = it->second; 131 Client* client = it->second;
117 132 if (ShouldStartRequest(request.get(), client)) {
118 bool is_synchronous = (url_request->load_flags() & net::LOAD_IGNORE_LIMITS) == 133 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 { 134 } else {
127 StartRequest(request.get(), client); 135 RequestQueue::Pointer pointer = client->pending_requests.Insert(
136 request.get(), url_request->priority());
137 request->set_queue_pointer(pointer);
128 } 138 }
129 return request.PassAs<ResourceThrottle>(); 139 return request.PassAs<ResourceThrottle>();
130 } 140 }
131 141
132 void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) { 142 void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) {
133 DCHECK(CalledOnValidThread()); 143 DCHECK(CalledOnValidThread());
134 if (ContainsKey(unowned_requests_, request)) { 144 if (ContainsKey(unowned_requests_, request)) {
135 unowned_requests_.erase(request); 145 unowned_requests_.erase(request);
136 return; 146 return;
137 } 147 }
138 148
139 ClientMap::iterator client_it = client_map_.find(request->client_id()); 149 ClientMap::iterator client_it = client_map_.find(request->client_id());
140 if (client_it == client_map_.end()) { 150 if (client_it == client_map_.end()) {
141 return; 151 return;
142 } 152 }
143 153
144 Client* client = client_it->second; 154 Client* client = client_it->second;
145 RequestSet::iterator request_it = client->in_flight_requests.find(request); 155
146 if (request_it == client->in_flight_requests.end()) { 156 if (request->queue_pointer().is_null()) {
147 bool removed = false;
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));
159 } else {
160 size_t erased = client->in_flight_requests.erase(request); 157 size_t erased = client->in_flight_requests.erase(request);
161 DCHECK(erased); 158 DCHECK(erased);
159 } else {
160 client->pending_requests.Erase(request->queue_pointer());
161 request->clear_queue_pointer();
162 DCHECK(!ContainsKey(client->in_flight_requests, request));
162 } 163 }
163 164
164 if (client->in_flight_requests.empty()) { 165 // Removing this request may have freed up another to load.
willchan no longer on Chromium 2013/03/18 07:30:33 Isn't this only true if we remove an in flight req
James Simonsen 2013/03/18 23:50:13 Done.
165 // Since the network is now idle, we may as well load some of the low 166 LoadPendingRequests(client);
166 // priority requests.
167 LoadPendingRequests(client);
168 }
169 } 167 }
170 168
171 void ResourceScheduler::OnClientCreated(int child_id, int route_id) { 169 void ResourceScheduler::OnClientCreated(int child_id, int route_id) {
172 DCHECK(CalledOnValidThread()); 170 DCHECK(CalledOnValidThread());
173 ClientId client_id = MakeClientId(child_id, route_id); 171 ClientId client_id = MakeClientId(child_id, route_id);
174 DCHECK(!ContainsKey(client_map_, client_id)); 172 DCHECK(!ContainsKey(client_map_, client_id));
175 173
176 client_map_[client_id] = new Client; 174 client_map_[client_id] = new Client;
177 } 175 }
178 176
179 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) { 177 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) {
180 DCHECK(CalledOnValidThread()); 178 DCHECK(CalledOnValidThread());
181 ClientId client_id = MakeClientId(child_id, route_id); 179 ClientId client_id = MakeClientId(child_id, route_id);
182 DCHECK(ContainsKey(client_map_, client_id)); 180 DCHECK(ContainsKey(client_map_, client_id));
183 ClientMap::iterator it = client_map_.find(client_id); 181 ClientMap::iterator it = client_map_.find(client_id);
184 Client* client = it->second; 182 Client* client = it->second;
185 183
186 // FYI, |in_flight_requests| should only be non-empty in the case of a 184 // FYI, |in_flight_requests| should only be non-empty in the case of a
187 // cross-renderer navigation. 185 // cross-renderer navigation.
willchan no longer on Chromium 2013/03/18 07:30:33 How does this work? We move all the requests into
James Simonsen 2013/03/18 23:50:13 It doesn't get picked up in the new renderer. I sh
willchan no longer on Chromium 2013/03/19 20:53:52 Do we want to ignore it in debug mode? Are there a
188 for (RequestSet::iterator it = client->in_flight_requests.begin(); 186 for (RequestSet::iterator it = client->in_flight_requests.begin();
189 it != client->in_flight_requests.end(); ++it) { 187 it != client->in_flight_requests.end(); ++it) {
190 unowned_requests_.insert(*it); 188 unowned_requests_.insert(*it);
191 } 189 }
192 client->in_flight_requests.clear(); 190 client->in_flight_requests.clear();
193 191
194 delete client; 192 delete client;
195 client_map_.erase(it); 193 client_map_.erase(it);
196 } 194 }
197 195
(...skipping 28 matching lines...) Expand all
226 LoadPendingRequests(client); 224 LoadPendingRequests(client);
227 } 225 }
228 } 226 }
229 227
230 void ResourceScheduler::StartRequest(ScheduledResourceRequest* request, 228 void ResourceScheduler::StartRequest(ScheduledResourceRequest* request,
231 Client* client) { 229 Client* client) {
232 client->in_flight_requests.insert(request); 230 client->in_flight_requests.insert(request);
233 request->Start(); 231 request->Start();
234 } 232 }
235 233
234 void ResourceScheduler::ReprioritizeRequest(ScheduledResourceRequest* request) {
235 ClientMap::iterator client_it = client_map_.find(request->client_id());
236 if (client_it == client_map_.end()) {
237 // The client was likely deleted shortly before we received this IPC.
willchan no longer on Chromium 2013/03/18 07:30:33 How? The comment for OnClientDeleted() says it hap
James Simonsen 2013/03/18 23:50:13 RenderViewHosts can be deleted by user action on t
willchan no longer on Chromium 2013/03/19 20:53:52 Why are we listening for the signal that the Rende
238 return;
239 }
240
241 Client *client = client_it->second;
242 if (request->queue_pointer().is_null()) {
243 DCHECK(ContainsKey(client->in_flight_requests, request));
244 // Request has already started.
willchan no longer on Chromium 2013/03/18 07:30:33 But don't we need to call set_priority()? OH, I se
James Simonsen 2013/03/18 23:50:13 Done.
245 return;
246 }
247
248 client->pending_requests.Erase(request->queue_pointer());
willchan no longer on Chromium 2013/03/18 07:30:33 When I see stuff like this where the caller needs
James Simonsen 2013/03/18 23:50:13 Done.
249 RequestQueue::Pointer pointer = client->pending_requests.Insert(
250 request, request->url_request().priority());
251 request->set_queue_pointer(pointer);
252
253 // Check if this request is now able to load at its new priority.
willchan no longer on Chromium 2013/03/18 07:30:33 We only want to do this if the new priority is hig
James Simonsen 2013/03/18 23:50:13 Done.
254 LoadPendingRequests(client);
255 }
256
236 void ResourceScheduler::LoadPendingRequests(Client* client) { 257 void ResourceScheduler::LoadPendingRequests(Client* client) {
237 while (!client->pending_requests.empty()) { 258 while (client->pending_requests.size()) {
238 ScheduledResourceRequest* request = client->pending_requests.front(); 259 ScheduledResourceRequest* request =
239 client->pending_requests.erase(client->pending_requests.begin()); 260 client->pending_requests.FirstMax().value();
240 StartRequest(request, client); 261 if (ShouldStartRequest(request, client)) {
262 client->pending_requests.Erase(request->queue_pointer());
263 request->clear_queue_pointer();
264 StartRequest(request, client);
265 } else {
266 break;
267 }
241 } 268 }
242 } 269 }
243 270
271 int ResourceScheduler::GetNumLowPriorityRequestsInFlight(Client* client) {
272 int count = 0;
273 for (RequestSet::iterator it = client->in_flight_requests.begin();
274 it != client->in_flight_requests.end(); ++it) {
275 if ((*it)->url_request().priority() < net::LOW) {
276 ++count;
277 }
278 }
279 return count;
280 }
281
282 bool ResourceScheduler::ShouldStartRequest(ScheduledResourceRequest* request,
willchan no longer on Chromium 2013/03/18 07:30:33 Please add a comment here explaining in plain Engl
James Simonsen 2013/03/18 23:50:13 Done.
283 Client* client) {
284 bool is_synchronous =
willchan no longer on Chromium 2013/03/18 07:30:33 I know you're just moving this, but this caught my
James Simonsen 2013/03/18 23:50:13 Done.
285 (request->url_request().load_flags() & net::LOAD_IGNORE_LIMITS) ==
286 net::LOAD_IGNORE_LIMITS;
287 bool is_low_priority =
willchan no longer on Chromium 2013/03/18 07:30:33 Sorry again for nitting you when you're just movin
James Simonsen 2013/03/18 23:50:13 Done.
288 request->url_request().priority() < net::LOW && !is_synchronous;
289 if (!is_low_priority)
290 return true;
291
292 int num_low_priority_requests_in_flight =
293 GetNumLowPriorityRequestsInFlight(client);
294 if (num_low_priority_requests_in_flight >=
295 kMaxNumLowPriorityRequestsPerClient) {
296 return false;
297 }
298
299 int num_high_priority_requests_in_flight =
willchan no longer on Chromium 2013/03/18 07:30:33 You don't need the number, you just need to know i
James Simonsen 2013/03/18 23:50:13 Done.
300 client->in_flight_requests.size() - num_low_priority_requests_in_flight;
301 if (num_high_priority_requests_in_flight && !client->has_body) {
302 return false;
303 }
304
305 return true;
306 }
307
244 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( 308 ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
245 int child_id, int route_id) { 309 int child_id, int route_id) {
246 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; 310 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id;
247 } 311 }
248 312
249 ResourceScheduler::Client::Client() 313 ResourceScheduler::Client::Client()
250 : has_body(false) { 314 : has_body(false),
315 pending_requests(net::NUM_PRIORITIES) {
251 } 316 }
252 317
253 ResourceScheduler::Client::~Client() { 318 ResourceScheduler::Client::~Client() {
254 DCHECK(in_flight_requests.empty()); 319 DCHECK(in_flight_requests.empty());
255 DCHECK(pending_requests.empty()); 320 DCHECK(!pending_requests.size());
256 } 321 }
257 322
258 } // namespace content 323 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698