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

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: Move Client back to .h -- broke Win 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 public:
26 ScheduledResourceRequest(const ClientId& client_id,
27 net::URLRequest* request,
28 ResourceScheduler* scheduler)
29 : ResourceMessageDelegate(request),
30 client_id_(client_id),
31 request_(request),
32 ready_(false),
33 deferred_(false),
34 scheduler_(scheduler) {
35 }
36
37 virtual ~ScheduledResourceRequest() {
38 scheduler_->RemoveRequest(this);
39 }
40
41 void Start() {
42 ready_ = true;
43 if (deferred_ && request_->status().is_success()) {
44 deferred_ = false;
45 controller()->Resume();
46 }
47 }
48
49 const ClientId& client_id() const { return client_id_; }
50 const net::URLRequest& url_request() const { return *request_; }
51
52 private:
53 // ResourceMessageDelegate interface:
54 virtual bool OnMessageReceived(const IPC::Message& message,
55 bool* message_was_ok) OVERRIDE {
56 bool handled = true;
57 IPC_BEGIN_MESSAGE_MAP_EX(ScheduledResourceRequest, message, *message_was_ok)
58 IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority)
59 IPC_MESSAGE_UNHANDLED(handled = false)
60 IPC_END_MESSAGE_MAP_EX()
61 return handled;
62 }
63
64 // ResourceThrottle interface:
65 virtual void WillStartRequest(bool* defer) OVERRIDE {
66 deferred_ = *defer = !ready_;
67 }
68
69 void DidChangePriority(int request_id, net::RequestPriority new_priority) {
70 net::RequestPriority old_priority = request_->priority();
71 request_->set_priority(new_priority);
72 if (new_priority > old_priority) {
73 Start();
74 }
75 }
76
77 ClientId client_id_;
78 net::URLRequest* request_;
79 bool ready_;
80 bool deferred_;
81 ResourceScheduler* scheduler_;
82
83 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest);
84 };
85
86 ResourceScheduler::ResourceScheduler()
87 : client_map_(kMaxNumNavigationsToTrack) {
88 }
89
90 ResourceScheduler::~ResourceScheduler() {
91 for (ClientMap::iterator it ALLOW_UNUSED = client_map_.begin();
92 it != client_map_.end(); ++it) {
93 DCHECK(it->second->pending_requests.empty());
94 DCHECK(it->second->in_flight_requests.empty());
95 }
96 DCHECK(unowned_requests_.empty());
97 }
98
99 scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest(
100 int child_id,
101 int route_id,
102 net::URLRequest* url_request) {
103 DCHECK(CalledOnValidThread());
104 ClientId client_id = MakeClientId(child_id, route_id);
105 scoped_ptr<ScheduledResourceRequest> request(
106 new ScheduledResourceRequest(client_id, url_request, this));
107
108 ClientMap::iterator it = client_map_.Get(client_id);
109 if (it == client_map_.end()) {
110 // There are several ways this could happen:
111 // 1. <a ping> requests don't have a route_id.
112 // 2. Most unittests don't send the IPCs needed to register Clients.
113 // 3. The tab is closed while a RequestResource IPC is in flight.
114 // 4. The tab hasn't navigated recently.
115 unowned_requests_.insert(request.get());
116 request->Start();
117 return request.PassAs<ResourceThrottle>();
118 }
119
120 Client* client = it->second;
121
122 bool is_synchronous = (url_request->load_flags() & net::LOAD_IGNORE_LIMITS) ==
123 net::LOAD_IGNORE_LIMITS;
124 bool is_low_priority =
125 url_request->priority() < net::MEDIUM && !is_synchronous;
126
127 if (is_low_priority && !client->in_flight_requests.empty() &&
128 !client->has_body) {
129 client->pending_requests.push_back(request.get());
130 } else {
131 StartRequest(request.get(), client);
132 }
133 return request.PassAs<ResourceThrottle>();
134 }
135
136 void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) {
137 DCHECK(CalledOnValidThread());
138 if (ContainsKey(unowned_requests_, request)) {
139 unowned_requests_.erase(request);
140 return;
141 }
142
143 ClientMap::iterator client_it = client_map_.Get(request->client_id());
144 if (client_it == client_map_.end()) {
145 return;
146 }
147
148 Client* client = client_it->second;
149 RequestSet::iterator request_it = client->in_flight_requests.find(request);
150 if (request_it == client->in_flight_requests.end()) {
151 bool removed = false;
152 RequestQueue::iterator queue_it;
153 for (queue_it = client->pending_requests.begin();
154 queue_it != client->pending_requests.end(); ++queue_it) {
155 if (*queue_it == request) {
156 client->pending_requests.erase(queue_it);
157 removed = true;
158 break;
159 }
160 }
161 DCHECK(removed);
162 DCHECK(!ContainsKey(client->in_flight_requests, request));
163 } else {
164 size_t erased = client->in_flight_requests.erase(request);
165 DCHECK(erased);
166 }
167
168 if (client->in_flight_requests.empty()) {
169 // Since the network is now idle, we may as well load some of the low
170 // priority requests.
171 LoadPendingRequests(client);
172 }
173 }
174
175 void ResourceScheduler::OnNavigate(int child_id, int route_id) {
176 DCHECK(CalledOnValidThread());
177 ClientId client_id = MakeClientId(child_id, route_id);
178
179 ClientMap::iterator it = client_map_.Get(client_id);
180 if (it == client_map_.end()) {
181 it = client_map_.Put(client_id, new Client(this));
182 }
183
184 Client* client = it->second;
185 client->has_body = false;
186 }
187
188 void ResourceScheduler::OnWillInsertBody(int child_id, int route_id) {
189 DCHECK(CalledOnValidThread());
190 ClientId client_id = MakeClientId(child_id, route_id);
191 ClientMap::iterator it = client_map_.Get(client_id);
192 if (it == client_map_.end()) {
193 return;
194 }
195
196 Client* client = it->second;
197 if (!client->has_body) {
198 client->has_body = true;
199 LoadPendingRequests(client);
200 }
201 }
202
203 void ResourceScheduler::StartRequest(ScheduledResourceRequest* request,
204 Client* client) {
205 client->in_flight_requests.insert(request);
206 request->Start();
207 }
208
209 void ResourceScheduler::LoadPendingRequests(Client* client) {
210 while (!client->pending_requests.empty()) {
211 ScheduledResourceRequest* request = client->pending_requests.front();
212 client->pending_requests.erase(client->pending_requests.begin());
213 StartRequest(request, client);
214 }
215 }
216
217 void ResourceScheduler::RemoveClient(Client* client) {
218 LoadPendingRequests(client);
219 for (RequestSet::iterator it = client->in_flight_requests.begin();
220 it != client->in_flight_requests.end(); ++it) {
221 unowned_requests_.insert(*it);
222 }
223 client->in_flight_requests.clear();
224 }
225
226 ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
227 int child_id, int route_id) {
228 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id;
229 }
230
231 ResourceScheduler::Client::Client(ResourceScheduler* scheduler)
232 : has_body(false),
233 scheduler_(scheduler) {
234 }
235
236 ResourceScheduler::Client::~Client() {
237 scheduler_->RemoveClient(this);
238 DCHECK(in_flight_requests.empty());
239 DCHECK(pending_requests.empty());
240 }
241
242 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/loader/resource_scheduler.h ('k') | content/browser/loader/resource_scheduler_filter.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698