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

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

Issue 11270027: Add a ResourceScheduler to ResourceDispatcherHost. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Use new WebKit signals Created 7 years, 10 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "content/browser/loader/resource_scheduler.h"
6
7 #include "base/stl_util.h"
8 #include "content/common/resource_messages.h"
9 #include "content/browser/loader/resource_message_delegate.h"
10 #include "content/public/browser/resource_controller.h"
11 #include "content/public/browser/resource_throttle.h"
12 #include "ipc/ipc_message_macros.h"
13 #include "net/base/load_flags.h"
14 #include "net/base/request_priority.h"
15 #include "net/url_request/url_request.h"
16
17 namespace content {
18
19 // TODO(simonjam): This is arbitrary. Experiment.
20 static const int kMaxNumNavigationsToTrack = 5;
21
22 class ResourceScheduler::ScheduledResourceRequest
23 : public ResourceMessageDelegate,
24 public ResourceThrottle {
25
darin (slow to review) 2013/02/19 06:58:33 nit: nix the new line here
James Simonsen 2013/02/19 23:08:58 Done.
26 public:
27 ScheduledResourceRequest(const ClientId& client_id,
28 net::URLRequest* request,
29 ResourceScheduler* scheduler)
30 : ResourceMessageDelegate(request),
31 client_id_(client_id),
32 request_(request),
33 ready_(false),
34 deferred_(false),
35 scheduler_(scheduler) {
36 }
37
38 virtual ~ScheduledResourceRequest() {
39 scheduler_->RemoveRequest(this);
40 }
41
42 void Start() {
43 ready_ = true;
44 if (deferred_ &&
45 request_->status().status() != net::URLRequestStatus::CANCELED) {
darin (slow to review) 2013/02/19 06:58:33 what about if the status is FAILED? why is CANCEL
James Simonsen 2013/02/19 23:08:58 Done. No good reason, just that's all that was nee
46 deferred_ = false;
47 controller()->Resume();
48 }
49 }
50
51 const ClientId& client_id() const { return client_id_; }
52 const net::URLRequest& url_request() const { return *request_; }
53
54 private:
55 // ResourceMessageDelegate interface:
56 virtual bool OnMessageReceived(const IPC::Message& message,
57 bool* message_was_ok) OVERRIDE {
58 bool handled = true;
59 IPC_BEGIN_MESSAGE_MAP_EX(ScheduledResourceRequest, message, *message_was_ok)
60 IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority)
61 IPC_MESSAGE_UNHANDLED(handled = false)
62 IPC_END_MESSAGE_MAP_EX()
63 return handled;
64 }
65
66 // ResourceThrottle interface:
67 virtual void WillStartRequest(bool* defer) OVERRIDE {
68 deferred_ = *defer = !ready_;
69 }
70
71 void DidChangePriority(int request_id, net::RequestPriority new_priority) {
72 net::RequestPriority old_priority = request_->priority();
73 request_->set_priority(new_priority);
74 if (new_priority > old_priority) {
75 Start();
76 }
77 }
78
79 ClientId client_id_;
80 net::URLRequest* request_;
81 bool ready_;
82 bool deferred_;
83 ResourceScheduler* scheduler_;
84
85 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest);
86 };
87
88 ResourceScheduler::ResourceScheduler()
89 : client_map_(kMaxNumNavigationsToTrack) {
90 DetachFromThread(); // Construction happens on the main thread.
darin (slow to review) 2013/02/19 06:58:33 ResourceDispatcherHostImpl also has an IO thread i
James Simonsen 2013/02/19 23:08:58 Done.
91 }
92
93 ResourceScheduler::~ResourceScheduler() {
94 DetachFromThread(); // Destruction happens on the main thread.
95 for (ClientMap::iterator it ALLOW_UNUSED = client_map_.begin();
96 it != client_map_.end(); ++it) {
97 DCHECK(it->second->pending_requests.empty());
98 DCHECK(it->second->in_flight_requests.empty());
99 }
100 DCHECK(unowned_requests_.empty());
101 }
102
103 scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest(
104 int child_id,
105 int route_id,
106 net::URLRequest* url_request) {
107 DCHECK(CalledOnValidThread());
108 ClientId client_id = MakeClientId(child_id, route_id);
109 scoped_ptr<ScheduledResourceRequest> request(
110 new ScheduledResourceRequest(client_id, url_request, this));
111
112 ClientMap::iterator it = client_map_.Get(client_id);
113 if (it == client_map_.end()) {
114 // There are several ways this could happen:
115 // 1. <a ping> requests don't have a route_id.
116 // 2. Most unittests don't send the IPCs needed to register Clients.
117 // 3. The tab is closed while a RequestResource IPC is in flight.
118 // 4. The tab hasn't navigated recently.
119 unowned_requests_.insert(request.get());
120 request->Start();
121 return request.PassAs<ResourceThrottle>();
122 }
123
124 Client* client = it->second;
125
126 bool is_synchronous = (url_request->load_flags() & net::LOAD_IGNORE_LIMITS) ==
127 net::LOAD_IGNORE_LIMITS;
128 bool is_low_priority =
129 url_request->priority() < net::MEDIUM && !is_synchronous;
130
131 if (is_low_priority && !client->in_flight_requests.empty() &&
132 !client->has_body) {
133 client->pending_requests.push_back(request.get());
134 } else {
135 StartRequest(request.get(), client);
136 }
137 return request.PassAs<ResourceThrottle>();
138 }
139
140 void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) {
141 DCHECK(CalledOnValidThread());
142 if (ContainsKey(unowned_requests_, request)) {
143 unowned_requests_.erase(request);
144 return;
145 }
146
147 ClientMap::iterator client_it = client_map_.Get(request->client_id());
148 if (client_it == client_map_.end()) {
149 return;
150 }
151
152 Client* client = client_it->second;
153 RequestSet::iterator request_it = client->in_flight_requests.find(request);
154 if (request_it == client->in_flight_requests.end()) {
155 bool removed = false;
156 RequestQueue::iterator queue_it;
157 for (queue_it = client->pending_requests.begin();
158 queue_it != client->pending_requests.end(); ++queue_it) {
159 if (*queue_it == request) {
160 client->pending_requests.erase(queue_it);
161 removed = true;
162 break;
163 }
164 }
165 DCHECK(removed);
166 DCHECK(!ContainsKey(client->in_flight_requests, request));
167 } else {
168 size_t erased = client->in_flight_requests.erase(request);
169 DCHECK(erased);
170 }
171
172 if (client->in_flight_requests.empty()) {
173 // Since the network is now idle, we may as well load some of the low
174 // priority requests.
175 LoadPendingRequests(client);
176 }
177 }
178
179 void ResourceScheduler::OnNavigate(int child_id, int route_id) {
180 DCHECK(CalledOnValidThread());
181 ClientId client_id = MakeClientId(child_id, route_id);
182
183 ClientMap::iterator it = client_map_.Get(client_id);
184 if (it == client_map_.end()) {
185 it = client_map_.Put(client_id, new Client(this));
186 }
187
188 Client* client = it->second;
189 client->has_body = false;
190 }
191
192 void ResourceScheduler::OnWillInsertBody(int child_id, int route_id) {
193 DCHECK(CalledOnValidThread());
194 ClientId client_id = MakeClientId(child_id, route_id);
195 ClientMap::iterator it = client_map_.Get(client_id);
196 if (it == client_map_.end()) {
197 return;
198 }
199
200 Client* client = it->second;
201 if (!client->has_body) {
202 client->has_body = true;
203 LoadPendingRequests(client);
204 }
205 }
206
207 void ResourceScheduler::StartRequest(ScheduledResourceRequest* request,
208 Client* client) {
209 client->in_flight_requests.insert(request);
210 request->Start();
211 }
212
213 void ResourceScheduler::LoadPendingRequests(Client* client) {
214 RequestQueue::iterator it;
215 while (!client->pending_requests.empty()) {
216 ScheduledResourceRequest* request = client->pending_requests.front();
217 client->pending_requests.erase(client->pending_requests.begin());
218 StartRequest(request, client);
219 }
220 }
221
222 void ResourceScheduler::RemoveClient(Client* client) {
223 LoadPendingRequests(client);
224 for (RequestSet::iterator it = client->in_flight_requests.begin();
225 it != client->in_flight_requests.end(); ++it) {
226 unowned_requests_.insert(*it);
227 }
228 client->in_flight_requests.clear();
229 }
230
231 ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
232 int child_id, int route_id) {
233 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id;
234 }
235
236 ResourceScheduler::Client::Client(ResourceScheduler* scheduler)
237 : has_body(false),
238 scheduler_(scheduler) {
239 }
240
241 ResourceScheduler::Client::~Client() {
242 scheduler_->RemoveClient(this);
243 DCHECK(in_flight_requests.empty());
244 DCHECK(pending_requests.empty());
245 }
246
247 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698