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

Side by Side Diff: chrome/browser/prerender/prerender_link_manager.cc

Issue 11551003: Change multi-prerender API to include per launcher slots. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: remediation Created 7 years, 11 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 "chrome/browser/prerender/prerender_link_manager.h" 5 #include "chrome/browser/prerender/prerender_link_manager.h"
6 6
7 #include <limits> 7 #include <limits>
8 #include <set>
8 #include <utility> 9 #include <utility>
9 10
10 #include "base/memory/scoped_ptr.h" 11 #include "base/memory/scoped_ptr.h"
11 #include "chrome/browser/prerender/prerender_contents.h" 12 #include "chrome/browser/prerender/prerender_contents.h"
12 #include "chrome/browser/prerender/prerender_handle.h" 13 #include "chrome/browser/prerender/prerender_handle.h"
13 #include "chrome/browser/prerender/prerender_manager.h" 14 #include "chrome/browser/prerender/prerender_manager.h"
14 #include "chrome/browser/prerender/prerender_manager_factory.h" 15 #include "chrome/browser/prerender/prerender_manager_factory.h"
15 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/prerender_messages.h" 17 #include "chrome/common/prerender_messages.h"
17 #include "content/public/browser/render_process_host.h" 18 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/render_view_host.h" 19 #include "content/public/browser/render_view_host.h"
19 #include "content/public/browser/session_storage_namespace.h" 20 #include "content/public/browser/session_storage_namespace.h"
20 #include "content/public/common/referrer.h" 21 #include "content/public/common/referrer.h"
21 #include "googleurl/src/gurl.h" 22 #include "googleurl/src/gurl.h"
22 #include "ui/gfx/size.h" 23 #include "ui/gfx/size.h"
23 24
25 using base::TimeDelta;
26 using base::TimeTicks;
24 using content::RenderViewHost; 27 using content::RenderViewHost;
25 using content::SessionStorageNamespace; 28 using content::SessionStorageNamespace;
26 29
27 namespace { 30 namespace {
28 31
29 void Send(int child_id, IPC::Message* raw_message) { 32 void Send(int child_id, IPC::Message* raw_message) {
30 using content::RenderProcessHost; 33 using content::RenderProcessHost;
31 scoped_ptr<IPC::Message> own_message(raw_message); 34 scoped_ptr<IPC::Message> own_message(raw_message);
32 35
33 RenderProcessHost* render_process_host = RenderProcessHost::FromID(child_id); 36 RenderProcessHost* render_process_host = RenderProcessHost::FromID(child_id);
34 if (!render_process_host) 37 if (!render_process_host)
35 return; 38 return;
36 render_process_host->Send(own_message.release()); 39 render_process_host->Send(own_message.release());
37 } 40 }
38 41
39 } // namespace 42 } // namespace
40 43
41 namespace prerender { 44 namespace prerender {
42 45
43 PrerenderLinkManager::PrerenderLinkManager(PrerenderManager* manager) 46 PrerenderLinkManager::PrerenderLinkManager(PrerenderManager* manager)
44 : manager_(manager) { 47 : has_shutdown_(false),
48 manager_(manager) {
45 } 49 }
46 50
47 PrerenderLinkManager::~PrerenderLinkManager() { 51 PrerenderLinkManager::~PrerenderLinkManager() {
48 for (IdPairToPrerenderHandleMap::iterator it = ids_to_handle_map_.begin(); 52 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
49 it != ids_to_handle_map_.end(); 53 i != prerenders_.end(); ++i) {
50 ++it) { 54 if (i->handle) {
51 PrerenderHandle* prerender_handle = it->second; 55 DCHECK(!i->handle->IsPrerendering())
52 DCHECK(!prerender_handle->IsPrerendering()) 56 << "All running prerenders should stop at the same time as the "
53 << "All running prerenders should stop at the same time as the " 57 << "PrerenderManager.";
54 << "PrerenderManager."; 58 delete i->handle;
55 delete prerender_handle; 59 i->handle = 0;
56 } 60 }
57 } 61 }
58 62 }
59 bool PrerenderLinkManager::OnAddPrerender(int child_id, 63
64 void PrerenderLinkManager::OnAddPrerender(int launcher_child_id,
60 int prerender_id, 65 int prerender_id,
61 const GURL& url, 66 const GURL& url,
62 const content::Referrer& referrer, 67 const content::Referrer& referrer,
63 const gfx::Size& size, 68 const gfx::Size& size,
64 int render_view_route_id) { 69 int render_view_route_id) {
mmenke 2012/12/28 20:15:14 optional: Maybe a DCHECK(!FindByLauncherChildIdAn
gavinp 2012/12/28 20:54:21 Done. I used DCHECK_EQ, just because I debug by pr
65 DVLOG(2) << "OnAddPrerender, child_id = " << child_id 70 LinkPrerender
66 << ", prerender_id = " << prerender_id 71 prerender(launcher_child_id, prerender_id, url, referrer, size,
67 << ", url = " << url.spec(); 72 render_view_route_id, manager_->GetCurrentTimeTicks());
68 DVLOG(3) << "... referrer url = " << referrer.url.spec() 73 prerenders_.push_back(prerender);
69 << ", size = (" << size.width() << ", " << size.height() << ")" 74 StartPrerenders();
70 << ", render_view_route_id = " << render_view_route_id;
71
72
73 PrerenderHandle* prerender_handle =
74 manager_->AddPrerenderFromLinkRelPrerender(
75 child_id, render_view_route_id, url, referrer, size);
76 if (!prerender_handle)
77 return false;
78
79 const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id);
80 DCHECK_EQ(0u, ids_to_handle_map_.count(child_and_prerender_id));
81 ids_to_handle_map_[child_and_prerender_id] = prerender_handle;
82
83 // If we are given a prerender that is already prerendering, we have missed
84 // the start event.
85 if (prerender_handle->IsPrerendering())
86 OnPrerenderStart(prerender_handle);
87 prerender_handle->SetObserver(this);
88 return true;
89 } 75 }
90 76
91 void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) { 77 void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) {
92 DVLOG(2) << "OnCancelPrerender, child_id = " << child_id 78 LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
93 << ", prerender_id = " << prerender_id; 79 prerender_id);
94 const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id); 80 if (!prerender)
95 IdPairToPrerenderHandleMap::iterator id_to_handle_iter = 81 return;
96 ids_to_handle_map_.find(child_and_prerender_id); 82
97 if (id_to_handle_iter == ids_to_handle_map_.end()) { 83 // Remove the handle from the PrerenderLinkManager before we cancel this
98 DVLOG(5) << "... canceling a prerender that doesn't exist."; 84 // prerender, to avoid reentering the PrerenderLinkManager, sending events to
99 return; 85 // the underlying prerender and making a second erase.
100 } 86 scoped_ptr<PrerenderHandle> own_prerender_handle(prerender->handle);
101 87 prerender->handle = NULL;
102 scoped_ptr<PrerenderHandle> prerender_handle(id_to_handle_iter->second); 88 RemovePrerender(prerender);
103 ids_to_handle_map_.erase(id_to_handle_iter); 89
104 prerender_handle->OnCancel(); 90 if (own_prerender_handle)
105 91 own_prerender_handle->OnCancel();
106 // Because OnCancel() can remove the prerender from the map, we need to 92
107 // consider our iterator invalid. 93 StartPrerenders();
108 id_to_handle_iter = ids_to_handle_map_.find(child_and_prerender_id);
109 if (id_to_handle_iter != ids_to_handle_map_.end())
110 RemovePrerender(id_to_handle_iter);
111 } 94 }
112 95
113 void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) { 96 void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) {
114 DVLOG(2) << "OnAbandonPrerender, child_id = " << child_id 97 LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
115 << ", prerender_id = " << prerender_id; 98 prerender_id);
116 const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id); 99 if (!prerender)
117 IdPairToPrerenderHandleMap::iterator id_to_handle_iter = 100 return;
118 ids_to_handle_map_.find(child_and_prerender_id); 101
119 if (id_to_handle_iter == ids_to_handle_map_.end()) 102 if (!prerender->handle) {
120 return; 103 RemovePrerender(prerender);
121 PrerenderHandle* prerender_handle = id_to_handle_iter->second; 104 return;
122 prerender_handle->OnNavigateAway(); 105 }
106
107 prerender->handle->OnNavigateAway();
108 DCHECK(prerender->handle);
109
110 // If the prerender is not running, remove it from the list so it does not
111 // leak. If it is running, it will send a cancel event when it stops which
112 // will remove it.
113 if (!prerender->handle->IsPrerendering())
114 RemovePrerender(prerender);
123 } 115 }
124 116
125 void PrerenderLinkManager::OnChannelClosing(int child_id) { 117 void PrerenderLinkManager::OnChannelClosing(int child_id) {
126 DVLOG(2) << "OnChannelClosing, child id = " << child_id; 118 std::list<LinkPrerender>::iterator next = prerenders_.begin();
127 const ChildAndPrerenderIdPair child_and_minimum_prerender_id( 119 while (next != prerenders_.end()) {
128 child_id, std::numeric_limits<int>::min()); 120 std::list<LinkPrerender>::iterator it = next;
129 const ChildAndPrerenderIdPair child_and_maximum_prerender_id(
130 child_id, std::numeric_limits<int>::max());
131
132 IdPairToPrerenderHandleMap::iterator
133 it = ids_to_handle_map_.lower_bound(child_and_minimum_prerender_id);
134 IdPairToPrerenderHandleMap::iterator
135 end = ids_to_handle_map_.upper_bound(child_and_maximum_prerender_id);
136 while (it != end) {
137 IdPairToPrerenderHandleMap::iterator next = it;
138 ++next; 121 ++next;
139 122
140 size_t size_before_abandon = ids_to_handle_map_.size(); 123 if (child_id != it->launcher_child_id)
141 OnAbandonPrerender(child_id, it->first.second); 124 continue;
142 DCHECK_EQ(size_before_abandon, ids_to_handle_map_.size()); 125
143 RemovePrerender(it); 126 const size_t running_prerender_count = CountRunningPrerenders();
144 127 OnAbandonPrerender(child_id, it->prerender_id);
145 it = next; 128 DCHECK_EQ(running_prerender_count, CountRunningPrerenders());
146 } 129 }
130 }
131
132 PrerenderLinkManager::LinkPrerender::LinkPrerender(
133 int launcher_child_id,
134 int prerender_id,
135 const GURL& url,
136 const content::Referrer& referrer,
137 const gfx::Size& size,
138 int render_view_route_id,
139 TimeTicks creation_time) : launcher_child_id(launcher_child_id),
140 prerender_id(prerender_id),
141 url(url),
142 referrer(referrer),
143 size(size),
144 render_view_route_id(render_view_route_id),
145 creation_time(creation_time),
146 handle(NULL) {
147 }
148
149 PrerenderLinkManager::LinkPrerender::~LinkPrerender() {
150 DCHECK_EQ(static_cast<PrerenderHandle*>(NULL), handle)
151 << "The PrerenderHandle should be destroyed before its Prerender.";
147 } 152 }
148 153
149 bool PrerenderLinkManager::IsEmpty() const { 154 bool PrerenderLinkManager::IsEmpty() const {
150 return ids_to_handle_map_.empty(); 155 return prerenders_.empty();
151 } 156 }
152 157
153 void PrerenderLinkManager::RemovePrerender( 158 size_t PrerenderLinkManager::CountRunningPrerenders() const {
154 const IdPairToPrerenderHandleMap::iterator& id_to_handle_iter) { 159 size_t retval = 0;
155 PrerenderHandle* prerender_handle = id_to_handle_iter->second; 160 for (std::list<LinkPrerender>::const_iterator i = prerenders_.begin();
156 delete prerender_handle; 161 i != prerenders_.end(); ++i) {
157 ids_to_handle_map_.erase(id_to_handle_iter); 162 if (i->handle && i->handle->IsPrerendering())
158 } 163 ++retval;
159 164 }
160 PrerenderLinkManager::IdPairToPrerenderHandleMap::iterator 165 return retval;
161 PrerenderLinkManager::FindPrerenderHandle( 166 }
162 PrerenderHandle* prerender_handle) { 167
163 for (IdPairToPrerenderHandleMap::iterator it = ids_to_handle_map_.begin(); 168 void PrerenderLinkManager::StartPrerenders() {
164 it != ids_to_handle_map_.end(); ++it) { 169 if (has_shutdown_)
165 if (it->second == prerender_handle) 170 return;
166 return it; 171
167 } 172 size_t total_started_prerender_count = 0;
168 return ids_to_handle_map_.end(); 173 std::multiset<std::pair<int, int> >
174 running_launcher_and_render_view_routes;
175
176 // Scan the list, counting how many prerenders have handles (and so were added
177 // to the PrerenderManager). The count is done for the system as a whole, and
178 // also per launcher.
179 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
180 i != prerenders_.end(); ++i) {
181 if (i->handle) {
182 ++total_started_prerender_count;
183 std::pair<int, int> launcher_and_render_view_route(
184 i->launcher_child_id, i->render_view_route_id);
185 running_launcher_and_render_view_routes.insert(
186 launcher_and_render_view_route);
187 DCHECK_GE(manager_->config().max_link_concurrency_per_launcher,
188 running_launcher_and_render_view_routes.count(
189 launcher_and_render_view_route));
190 }
191
192 std::pair<int, int> launcher_and_prerender_id(i->launcher_child_id,
193 i->prerender_id);
mmenke 2012/12/28 20:15:14 This isn't doing anything.
gavinp 2012/12/28 20:54:21 Yes. Amazing that it compiled, isn't it? I'm using
194 }
195 DCHECK_GE(manager_->config().max_link_concurrency,
196 total_started_prerender_count);
197 DCHECK_LE(CountRunningPrerenders(), total_started_prerender_count);
198
199 TimeTicks now = manager_->GetCurrentTimeTicks();
200
201 // Scan the list again, starting prerenders as our counts allow.
202 std::list<LinkPrerender>::iterator next = prerenders_.begin();
203 while (next != prerenders_.end()) {
204 std::list<LinkPrerender>::iterator i = next;
205 ++next;
206
207 if (total_started_prerender_count >=
208 manager_->config().max_link_concurrency ||
209 total_started_prerender_count >= prerenders_.size()) {
210 // The system is already at its prerender concurrency limit.
211 return;
212 }
213
214 if (i->handle) {
215 // This prerender has already been added to the prerender manager.
216 continue;
217 }
218
219 TimeDelta prerender_age = now - i->creation_time;
220 if (prerender_age >= manager_->config().max_wait_to_launch) {
221 // This prerender waited too long in the queue before launching.
222 prerenders_.erase(i);
223 continue;
224 }
225
226 std::pair<int, int> launcher_and_render_view_route(
227 i->launcher_child_id, i->render_view_route_id);
228 if (manager_->config().max_link_concurrency_per_launcher <=
229 running_launcher_and_render_view_routes.count(
230 launcher_and_render_view_route)) {
231 // This prerender's launcher is already at its limit.
232 continue;
233 }
234
235 PrerenderHandle* handle = manager_->AddPrerenderFromLinkRelPrerender(
236 i->launcher_child_id, i->render_view_route_id,
237 i->url, i->referrer, i->size);
238 if (!handle) {
239 // This prerender couldn't be launched, it's gone.
240 prerenders_.erase(i);
241 continue;
242 }
243
244 // We have successfully started a new prerender.
245 i->handle = handle;
246 ++total_started_prerender_count;
247 handle->SetObserver(this);
248 if (handle->IsPrerendering())
249 OnPrerenderStart(handle);
250
251 running_launcher_and_render_view_routes.insert(
252 launcher_and_render_view_route);
253 }
254 }
255
256 PrerenderLinkManager::LinkPrerender*
257 PrerenderLinkManager::FindByLauncherChildIdAndPrerenderId(int launcher_child_id,
258 int prerender_id) {
259 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
260 i != prerenders_.end(); ++i) {
261 if (launcher_child_id == i->launcher_child_id &&
262 prerender_id == i->prerender_id) {
263 return &(*i);
264 }
265 }
266 return NULL;
267 }
268
269 PrerenderLinkManager::LinkPrerender*
270 PrerenderLinkManager::FindByPrerenderHandle(PrerenderHandle* prerender_handle) {
271 DCHECK(prerender_handle);
272 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
273 i != prerenders_.end(); ++i) {
274 if (prerender_handle == i->handle)
275 return &(*i);
276 }
277 return NULL;
278 }
279
280 void PrerenderLinkManager::RemovePrerender(LinkPrerender* prerender) {
281 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
282 i != prerenders_.end(); ++i) {
283 if (&(*i) == prerender) {
284 scoped_ptr<PrerenderHandle> own_handle(i->handle);
285 i->handle = NULL;
286 prerenders_.erase(i);
287 return;
288 }
289 }
290 NOTREACHED();
291 }
292
293 void PrerenderLinkManager::Shutdown() {
294 has_shutdown_ = true;
169 } 295 }
170 296
171 // In practice, this is always called from either 297 // In practice, this is always called from either
172 // PrerenderLinkManager::OnAddPrerender in the regular case, or in the pending 298 // PrerenderLinkManager::OnAddPrerender in the regular case, or in the pending
173 // prerender case, from PrerenderHandle::AdoptPrerenderDataFrom. 299 // prerender case, from PrerenderHandle::AdoptPrerenderDataFrom.
174 void PrerenderLinkManager::OnPrerenderStart( 300 void PrerenderLinkManager::OnPrerenderStart(
175 PrerenderHandle* prerender_handle) { 301 PrerenderHandle* prerender_handle) {
176 IdPairToPrerenderHandleMap::iterator it = 302 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
177 FindPrerenderHandle(prerender_handle); 303 if (!prerender)
178 DCHECK(it != ids_to_handle_map_.end()); 304 return;
179 const int child_id = it->first.first; 305 Send(prerender->launcher_child_id,
180 const int prerender_id = it->first.second; 306 new PrerenderMsg_OnPrerenderStart(prerender->prerender_id));
181
182 Send(child_id, new PrerenderMsg_OnPrerenderStart(prerender_id));
183 } 307 }
184 308
185 void PrerenderLinkManager::OnPrerenderAddAlias( 309 void PrerenderLinkManager::OnPrerenderAddAlias(
186 PrerenderHandle* prerender_handle, 310 PrerenderHandle* prerender_handle,
187 const GURL& alias_url) { 311 const GURL& alias_url) {
188 IdPairToPrerenderHandleMap::iterator it = 312 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
189 FindPrerenderHandle(prerender_handle); 313 if (!prerender)
190 if (it == ids_to_handle_map_.end())
191 return; 314 return;
192 const int child_id = it->first.first; 315 Send(prerender->launcher_child_id,
193 const int prerender_id = it->first.second; 316 new PrerenderMsg_OnPrerenderAddAlias(prerender->prerender_id,
194 317 alias_url));
195 Send(child_id, new PrerenderMsg_OnPrerenderAddAlias(prerender_id, alias_url));
196 } 318 }
197 319
198 void PrerenderLinkManager::OnPrerenderStop( 320 void PrerenderLinkManager::OnPrerenderStop(
199 PrerenderHandle* prerender_handle) { 321 PrerenderHandle* prerender_handle) {
200 IdPairToPrerenderHandleMap::iterator it = 322 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
201 FindPrerenderHandle(prerender_handle); 323 if (!prerender)
202 if (it == ids_to_handle_map_.end())
203 return; 324 return;
204 const int child_id = it->first.first;
205 const int prerender_id = it->first.second;
206 325
207 Send(child_id, new PrerenderMsg_OnPrerenderStop(prerender_id)); 326 Send(prerender->launcher_child_id,
208 RemovePrerender(it); 327 new PrerenderMsg_OnPrerenderStop(prerender->prerender_id));
328 RemovePrerender(prerender);
329 StartPrerenders();
209 } 330 }
210 331
211 } // namespace prerender 332 } // namespace prerender
OLDNEW
« no previous file with comments | « chrome/browser/prerender/prerender_link_manager.h ('k') | chrome/browser/prerender/prerender_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698