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

Side by Side Diff: chrome/browser/net/predictor.cc

Issue 7467012: Modifying prefetch to account for multi-profile. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 4 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) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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/net/predictor.h" 5 #include "chrome/browser/net/predictor.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath> 8 #include <cmath>
9 #include <set> 9 #include <set>
10 #include <sstream> 10 #include <sstream>
11 11
12 #include "base/bind.h"
13 #include "base/command_line.h"
12 #include "base/compiler_specific.h" 14 #include "base/compiler_specific.h"
13 #include "base/metrics/histogram.h" 15 #include "base/metrics/histogram.h"
14 #include "base/stringprintf.h" 16 #include "base/stringprintf.h"
17 #include "base/synchronization/waitable_event.h"
15 #include "base/time.h" 18 #include "base/time.h"
16 #include "base/values.h" 19 #include "base/values.h"
20 #include "chrome/browser/browser_process.h"
willchan no longer on Chromium 2011/08/12 21:51:47 Please eliminate the need for this include by remo
rpetterson 2011/08/13 00:55:17 Done.
21 #include "chrome/browser/io_thread.h"
17 #include "chrome/browser/net/preconnect.h" 22 #include "chrome/browser/net/preconnect.h"
23 #include "chrome/browser/prefs/browser_prefs.h"
24 #include "chrome/browser/prefs/pref_service.h"
25 #include "chrome/browser/prefs/scoped_user_pref_update.h"
26 #include "chrome/browser/prefs/session_startup_pref.h"
27 #include "chrome/browser/profiles/profile.h"
willchan no longer on Chromium 2011/08/12 21:51:47 Is this header needed?
rpetterson 2011/08/13 00:55:17 Done.
28 #include "chrome/common/chrome_switches.h"
29 #include "chrome/common/pref_names.h"
18 #include "content/browser/browser_thread.h" 30 #include "content/browser/browser_thread.h"
19 #include "net/base/address_list.h" 31 #include "net/base/address_list.h"
20 #include "net/base/completion_callback.h" 32 #include "net/base/completion_callback.h"
21 #include "net/base/host_port_pair.h" 33 #include "net/base/host_port_pair.h"
22 #include "net/base/host_resolver.h" 34 #include "net/base/host_resolver.h"
23 #include "net/base/net_errors.h" 35 #include "net/base/net_errors.h"
24 #include "net/base/net_log.h" 36 #include "net/base/net_log.h"
25 #include "net/base/single_request_host_resolver.h" 37 #include "net/base/single_request_host_resolver.h"
26 38
27 using base::TimeDelta; 39 using base::TimeDelta;
28 40
29 namespace chrome_browser_net { 41 namespace chrome_browser_net {
30 42
43 static void DnsPrefetchMotivatedList(const UrlList& urls,
44 UrlInfo::ResolutionMotivation motivation);
31 // static 45 // static
32 const double Predictor::kPreconnectWorthyExpectedValue = 0.8; 46 const double Predictor::kPreconnectWorthyExpectedValue = 0.8;
33 // static 47 // static
34 const double Predictor::kDNSPreresolutionWorthyExpectedValue = 0.1; 48 const double Predictor::kDNSPreresolutionWorthyExpectedValue = 0.1;
35 // static 49 // static
36 const double Predictor::kDiscardableExpectedValue = 0.05; 50 const double Predictor::kDiscardableExpectedValue = 0.05;
37 // The goal is of trimming is to to reduce the importance (number of expected 51 // The goal is of trimming is to to reduce the importance (number of expected
38 // subresources needed) by a factor of 2 after about 24 hours of uptime. We will 52 // subresources needed) by a factor of 2 after about 24 hours of uptime. We will
39 // trim roughly once-an-hour of uptime. The ratio to use in each trim operation 53 // trim roughly once-an-hour of uptime. The ratio to use in each trim operation
40 // is then the 24th root of 0.5. If a user only surfs for 4 hours a day, then 54 // is then the 24th root of 0.5. If a user only surfs for 4 hours a day, then
41 // after about 6 days they will have halved all their estimates of subresource 55 // after about 6 days they will have halved all their estimates of subresource
42 // connections. Once this falls below kDiscardableExpectedValue the referrer 56 // connections. Once this falls below kDiscardableExpectedValue the referrer
43 // will be discarded. 57 // will be discarded.
44 // TODO(jar): Measure size of referrer lists in the field. Consider an adaptive 58 // TODO(jar): Measure size of referrer lists in the field. Consider an adaptive
45 // system that uses a higher trim ratio when the list is large. 59 // system that uses a higher trim ratio when the list is large.
46 // static 60 // static
47 const double Predictor::kReferrerTrimRatio = 0.97153; 61 const double Predictor::kReferrerTrimRatio = 0.97153;
48 62
49 // static 63 // static
50 const TimeDelta Predictor::kDurationBetweenTrimmings = TimeDelta::FromHours(1); 64 const TimeDelta Predictor::kDurationBetweenTrimmings = TimeDelta::FromHours(1);
51 // static 65 // static
52 const TimeDelta Predictor::kDurationBetweenTrimmingIncrements = 66 const TimeDelta Predictor::kDurationBetweenTrimmingIncrements =
53 TimeDelta::FromSeconds(15); 67 TimeDelta::FromSeconds(15);
54 // static 68 // static
55 const size_t Predictor::kUrlsTrimmedPerIncrement = 5u; 69 const size_t Predictor::kUrlsTrimmedPerIncrement = 5u;
70 // static
71 const size_t Predictor::kMaxSpeculativeParallelResolves = 3;
72 // To control our congestion avoidance system, which discards a queue when
73 // resolutions are "taking too long," we need an expected resolution time.
74 // Common average is in the range of 300-500ms.
75 const int kExpectedResolutionTimeMs = 500;
76 // static
77 const int Predictor::kTypicalSpeculativeGroupSize = 8;
78 // static
79 const int Predictor::kMaxSpeculativeResolveQueueDelayMs =
80 (kExpectedResolutionTimeMs * Predictor::kTypicalSpeculativeGroupSize) /
81 Predictor::kMaxSpeculativeParallelResolves;
82
83 static int g_max_queueing_delay_ms;
willchan no longer on Chromium 2011/08/12 21:51:47 Please initialize this
rpetterson 2011/08/13 00:55:17 Done.
84 static size_t g_max_parallel_resolves = 0u;
85
86 // A version number for prefs that are saved. This should be incremented when
87 // we change the format so that we discard old data.
88 static const int kPredictorStartupFormatVersion = 1;
56 89
57 class Predictor::LookupRequest { 90 class Predictor::LookupRequest {
58 public: 91 public:
59 LookupRequest(Predictor* predictor, 92 LookupRequest(Predictor* predictor,
60 net::HostResolver* host_resolver, 93 net::HostResolver* host_resolver,
61 const GURL& url) 94 const GURL& url)
62 : ALLOW_THIS_IN_INITIALIZER_LIST( 95 : ALLOW_THIS_IN_INITIALIZER_LIST(
63 net_callback_(this, &LookupRequest::OnLookupFinished)), 96 net_callback_(this, &LookupRequest::OnLookupFinished)),
64 predictor_(predictor), 97 predictor_(predictor),
65 url_(url), 98 url_(url),
(...skipping 26 matching lines...) Expand all
92 125
93 Predictor* predictor_; // The predictor which started us. 126 Predictor* predictor_; // The predictor which started us.
94 127
95 const GURL url_; // Hostname to resolve. 128 const GURL url_; // Hostname to resolve.
96 net::SingleRequestHostResolver resolver_; 129 net::SingleRequestHostResolver resolver_;
97 net::AddressList addresses_; 130 net::AddressList addresses_;
98 131
99 DISALLOW_COPY_AND_ASSIGN(LookupRequest); 132 DISALLOW_COPY_AND_ASSIGN(LookupRequest);
100 }; 133 };
101 134
102 Predictor::Predictor(net::HostResolver* host_resolver, 135 Predictor::Predictor()
103 TimeDelta max_dns_queue_delay, 136 : predictor_enabled_(true),
104 size_t max_concurrent, 137 peak_pending_lookups_(0),
105 bool preconnect_enabled)
106 : peak_pending_lookups_(0),
107 shutdown_(false), 138 shutdown_(false),
108 max_concurrent_dns_lookups_(max_concurrent), 139 max_concurrent_dns_lookups_(g_max_parallel_resolves),
109 max_dns_queue_delay_(max_dns_queue_delay), 140 max_dns_queue_delay_(
110 host_resolver_(host_resolver), 141 TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)),
111 preconnect_enabled_(preconnect_enabled), 142 host_resolver_(NULL),
143 preconnect_enabled_(true),
112 consecutive_omnibox_preconnect_count_(0), 144 consecutive_omnibox_preconnect_count_(0),
113 next_trim_time_(base::TimeTicks::Now() + kDurationBetweenTrimmings), 145 next_trim_time_(base::TimeTicks::Now() + kDurationBetweenTrimmings) {
114 ALLOW_THIS_IN_INITIALIZER_LIST(trim_task_factory_(this)) { 146 initial_observer_.reset(new InitialObserver());
willchan no longer on Chromium 2011/08/12 21:51:47 Can this be initialized in the initializer list?
rpetterson 2011/08/13 00:55:17 Done.
147
148 const CommandLine* command_line = CommandLine::ForCurrentProcess();
willchan no longer on Chromium 2011/08/12 21:51:47 As I mentioned earlier, I prefer explicitly passin
rpetterson 2011/08/13 00:55:17 Checked with jar -- command line takes precedence.
149 if (command_line->HasSwitch(switches::kDisablePreconnect))
150 preconnect_enabled_ = false;
151 else if (command_line->HasSwitch(switches::kEnablePreconnect))
152 preconnect_enabled_ = true;
115 } 153 }
116 154
117 Predictor::~Predictor() { 155 Predictor::~Predictor() {
willchan no longer on Chromium 2011/08/12 21:51:47 Please DCHECK BrowserThread::IO
rpetterson 2011/08/13 00:55:17 Done.
118 DCHECK(shutdown_); 156 DCHECK(shutdown_);
119 } 157 }
120 158
121 void Predictor::Shutdown() { 159 // --------------------- Start UI methods. ------------------------------------
122 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
123 DCHECK(!shutdown_);
124 shutdown_ = true;
125 160
126 std::set<LookupRequest*>::iterator it; 161 void Predictor::InitNetworkPredictor(PrefService* user_prefs) {
willchan no longer on Chromium 2011/08/12 21:51:47 Please take PrefService* local_state and IOThread*
rpetterson 2011/08/13 00:55:17 Done.
127 for (it = pending_lookups_.begin(); it != pending_lookups_.end(); ++it) 162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
128 delete *it; 163
164 predictor_enabled_ = user_prefs->GetBoolean(prefs::kNetworkPredictionEnabled);
165
166 // Gather the list of hostnames to prefetch on startup.
167 PrefService* local_state = g_browser_process->local_state();
168 UrlList urls = GetPredictedUrlListAtStartup(user_prefs, local_state);
169
170 base::ListValue* referral_list =
171 static_cast<base::ListValue*>(user_prefs->GetList(
172 prefs::kDnsPrefetchingHostReferralList)->DeepCopy());
173
174 // Remove obsolete preferences from local state if necessary.
175 int current_version =
176 local_state->GetInteger(prefs::kMultipleProfilePrefMigration);
177 if ((current_version & browser::DNS_PREFS) == 0) {
178 local_state->RegisterListPref(prefs::kDnsStartupPrefetchList,
179 PrefService::UNSYNCABLE_PREF);
180 local_state->RegisterListPref(prefs::kDnsHostReferralList,
181 PrefService::UNSYNCABLE_PREF);
182 local_state->ClearPref(prefs::kDnsStartupPrefetchList);
183 local_state->ClearPref(prefs::kDnsHostReferralList);
184 local_state->SetInteger(prefs::kMultipleProfilePrefMigration,
185 current_version | browser::DNS_PREFS);
186 }
187
188 BrowserThread::PostTask(
189 BrowserThread::IO,
190 FROM_HERE,
191 base::Bind(
192 &Predictor::FinalizeInitializationOnIOThread,
193 base::Unretained(this),
194 urls, referral_list,
195 g_browser_process->io_thread()));
129 } 196 }
130 197
131 // Overloaded Resolve() to take a vector of names. 198 void Predictor::AnticipateOmniboxUrl(const GURL& url, bool preconnectable) {
132 void Predictor::ResolveList(const UrlList& urls, 199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
133 UrlInfo::ResolutionMotivation motivation) { 200 if (!predictor_enabled_)
134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
135
136 for (UrlList::const_iterator it = urls.begin(); it < urls.end(); ++it) {
137 AppendToResolutionQueue(*it, motivation);
138 }
139 }
140
141 // Basic Resolve() takes an invidual name, and adds it
142 // to the queue.
143 void Predictor::Resolve(const GURL& url,
144 UrlInfo::ResolutionMotivation motivation) {
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
146 if (!url.has_host())
147 return; 201 return;
148 AppendToResolutionQueue(url, motivation); 202 if (!url.is_valid() || !url.has_host())
149 } 203 return;
150
151 void Predictor::LearnFromNavigation(const GURL& referring_url,
152 const GURL& target_url) {
153 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
154 DCHECK_EQ(referring_url, Predictor::CanonicalizeUrl(referring_url));
155 DCHECK_NE(referring_url, GURL::EmptyGURL());
156 DCHECK_EQ(target_url, Predictor::CanonicalizeUrl(target_url));
157 DCHECK_NE(target_url, GURL::EmptyGURL());
158
159 referrers_[referring_url].SuggestHost(target_url);
160 // Possibly do some referrer trimming.
161 TrimReferrers();
162 }
163
164 enum SubresourceValue {
165 PRECONNECTION,
166 PRERESOLUTION,
167 TOO_NEW,
168 SUBRESOURCE_VALUE_MAX
169 };
170
171 void Predictor::AnticipateOmniboxUrl(const GURL& url, bool preconnectable) {
172 std::string host = url.HostNoBrackets(); 204 std::string host = url.HostNoBrackets();
173 bool is_new_host_request = (host != last_omnibox_host_); 205 bool is_new_host_request = (host != last_omnibox_host_);
174 last_omnibox_host_ = host; 206 last_omnibox_host_ = host;
175 207
176 UrlInfo::ResolutionMotivation motivation(UrlInfo::OMNIBOX_MOTIVATED); 208 UrlInfo::ResolutionMotivation motivation(UrlInfo::OMNIBOX_MOTIVATED);
177 base::TimeTicks now = base::TimeTicks::Now(); 209 base::TimeTicks now = base::TimeTicks::Now();
178 210
179 if (preconnect_enabled()) { 211 if (preconnect_enabled()) {
180 if (preconnectable && !is_new_host_request) { 212 if (preconnectable && !is_new_host_request) {
181 ++consecutive_omnibox_preconnect_count_; 213 ++consecutive_omnibox_preconnect_count_;
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
225 const int kMinPreresolveSeconds(10); 257 const int kMinPreresolveSeconds(10);
226 if (kMinPreresolveSeconds > (now - last_omnibox_preresolve_).InSeconds()) 258 if (kMinPreresolveSeconds > (now - last_omnibox_preresolve_).InSeconds())
227 return; 259 return;
228 } 260 }
229 last_omnibox_preresolve_ = now; 261 last_omnibox_preresolve_ = now;
230 262
231 // Perform at least DNS pre-resolution. 263 // Perform at least DNS pre-resolution.
232 BrowserThread::PostTask( 264 BrowserThread::PostTask(
233 BrowserThread::IO, 265 BrowserThread::IO,
234 FROM_HERE, 266 FROM_HERE,
235 NewRunnableMethod(this, &Predictor::Resolve, CanonicalizeUrl(url), 267 base::Bind(&Predictor::Resolve, base::Unretained(this),
236 motivation)); 268 CanonicalizeUrl(url), motivation));
237 } 269 }
238 270
239 void Predictor::PreconnectUrlAndSubresources(const GURL& url) { 271 void Predictor::PreconnectUrlAndSubresources(const GURL& url) {
272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
273 if (!predictor_enabled_)
274 return;
275 if (!url.is_valid() || !url.has_host())
276 return;
240 if (preconnect_enabled()) { 277 if (preconnect_enabled()) {
241 std::string host = url.HostNoBrackets(); 278 std::string host = url.HostNoBrackets();
242 UrlInfo::ResolutionMotivation motivation(UrlInfo::EARLY_LOAD_MOTIVATED); 279 UrlInfo::ResolutionMotivation motivation(UrlInfo::EARLY_LOAD_MOTIVATED);
243 const int kConnectionsNeeded = 1; 280 const int kConnectionsNeeded = 1;
244 PreconnectOnUIThread(CanonicalizeUrl(url), motivation, 281 PreconnectOnUIThread(CanonicalizeUrl(url), motivation,
245 kConnectionsNeeded); 282 kConnectionsNeeded);
246 PredictFrameSubresources(url.GetWithEmptyPath()); 283 PredictFrameSubresources(url.GetWithEmptyPath());
247 } 284 }
248 } 285 }
249 286
250 void Predictor::PredictFrameSubresources(const GURL& url) { 287 void Predictor::PredictFrameSubresources(const GURL& url) {
willchan no longer on Chromium 2011/08/12 21:51:47 Please DCHECK BrowserThread::UI
rpetterson 2011/08/13 00:55:17 This is not always called from the UI thread. For
288 if (!predictor_enabled_)
289 return;
251 DCHECK_EQ(url.GetWithEmptyPath(), url); 290 DCHECK_EQ(url.GetWithEmptyPath(), url);
252 // Add one pass through the message loop to allow current navigation to 291 // Add one pass through the message loop to allow current navigation to
253 // proceed. 292 // proceed.
254 BrowserThread::PostTask( 293 BrowserThread::PostTask(
255 BrowserThread::IO, 294 BrowserThread::IO,
256 FROM_HERE, 295 FROM_HERE,
257 NewRunnableMethod(this, &Predictor::PrepareFrameSubresources, url)); 296 base::Bind(&Predictor::PrepareFrameSubresources,
297 base::Unretained(this), url));
258 } 298 }
259 299
260 void Predictor::PrepareFrameSubresources(const GURL& url) { 300 UrlList Predictor::GetPredictedUrlListAtStartup(
261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 301 PrefService* user_prefs,
262 DCHECK_EQ(url.GetWithEmptyPath(), url); 302 PrefService* local_state) {
263 Referrers::iterator it = referrers_.find(url); 303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
264 if (referrers_.end() == it) { 304 UrlList urls;
265 // Only when we don't know anything about this url, make 2 connections 305 // Recall list of URLs we learned about during last session.
266 // available. We could do this completely via learning (by prepopulating 306 // This may catch secondary hostnames, pulled in by the homepages. It will
267 // the referrer_ list with this expected value), but it would swell the 307 // also catch more of the "primary" home pages, since that was (presumably)
268 // size of the list with all the "Leaf" nodes in the tree (nodes that don't 308 // rendered first (and will be rendered first this time too).
269 // load any subresources). If we learn about this resource, we will instead 309 const ListValue* startup_list =
270 // provide a more carefully estimated preconnection count. 310 user_prefs->GetList(prefs::kDnsPrefetchingStartupList);
271 if (preconnect_enabled_) 311
272 PreconnectOnIOThread(url, UrlInfo::SELF_REFERAL_MOTIVATED, 2); 312 if (startup_list) {
273 return; 313 base::ListValue::const_iterator it = startup_list->begin();
314 int format_version = -1;
315 if (it != startup_list->end() &&
316 (*it)->GetAsInteger(&format_version) &&
317 format_version == kPredictorStartupFormatVersion) {
318 ++it;
319 for (; it != startup_list->end(); ++it) {
320 std::string url_spec;
321 if (!(*it)->GetAsString(&url_spec)) {
322 LOG(DFATAL);
323 break; // Format incompatibility.
324 }
325 GURL url(url_spec);
326 if (!url.has_host() || !url.has_scheme()) {
327 LOG(DFATAL);
328 break; // Format incompatibility.
329 }
330
331 urls.push_back(url);
332 }
333 }
274 } 334 }
275 335
276 Referrer* referrer = &(it->second); 336 // Prepare for any static home page(s) the user has in prefs. The user may
277 referrer->IncrementUseCount(); 337 // have a LOT of tab's specified, so we may as well try to warm them all.
278 const UrlInfo::ResolutionMotivation motivation = 338 SessionStartupPref tab_start_pref =
279 UrlInfo::LEARNED_REFERAL_MOTIVATED; 339 SessionStartupPref::GetStartupPref(user_prefs);
280 for (Referrer::iterator future_url = referrer->begin(); 340 if (SessionStartupPref::URLS == tab_start_pref.type) {
281 future_url != referrer->end(); ++future_url) { 341 for (size_t i = 0; i < tab_start_pref.urls.size(); i++) {
282 SubresourceValue evalution(TOO_NEW); 342 GURL gurl = tab_start_pref.urls[i];
283 double connection_expectation = future_url->second.subresource_use_rate(); 343 if (!gurl.is_valid() || gurl.SchemeIsFile() || gurl.host().empty())
284 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.PreconnectSubresourceExpectation", 344 continue;
285 static_cast<int>(connection_expectation * 100), 345 if (gurl.SchemeIs("http") || gurl.SchemeIs("https"))
286 10, 5000, 50); 346 urls.push_back(gurl.GetWithEmptyPath());
287 future_url->second.ReferrerWasObserved();
288 if (preconnect_enabled_ &&
289 connection_expectation > kPreconnectWorthyExpectedValue) {
290 evalution = PRECONNECTION;
291 future_url->second.IncrementPreconnectionCount();
292 int count = static_cast<int>(std::ceil(connection_expectation));
293 if (url.host() == future_url->first.host())
294 ++count;
295 PreconnectOnIOThread(future_url->first, motivation, count);
296 } else if (connection_expectation > kDNSPreresolutionWorthyExpectedValue) {
297 evalution = PRERESOLUTION;
298 future_url->second.preresolution_increment();
299 UrlInfo* queued_info = AppendToResolutionQueue(future_url->first,
300 motivation);
301 if (queued_info)
302 queued_info->SetReferringHostname(url);
303 } 347 }
304 UMA_HISTOGRAM_ENUMERATION("Net.PreconnectSubresourceEval", evalution,
305 SUBRESOURCE_VALUE_MAX);
306 } 348 }
349
350 if (urls.empty())
351 urls.push_back(GURL("http://www.google.com:80"));
352
353 return urls;
354 }
355
356 void Predictor::set_max_queueing_delay(int max_queueing_delay_ms) {
willchan no longer on Chromium 2011/08/12 21:51:47 Please DCHECK BrowserThread::UI
rpetterson 2011/08/13 00:55:17 Done.
357 g_max_queueing_delay_ms = max_queueing_delay_ms;
358 }
359
360 void Predictor::set_max_parallel_resolves(size_t max_parallel_resolves) {
willchan no longer on Chromium 2011/08/12 21:51:47 Please DCHECK BrowserThread::UI
rpetterson 2011/08/13 00:55:17 Done.
361 g_max_parallel_resolves = max_parallel_resolves;
362 }
363
364
365 void Predictor::RegisterUserPrefs(PrefService* user_prefs) {
willchan no longer on Chromium 2011/08/12 21:51:47 Please DCHECK BrowserThread::UI
rpetterson 2011/08/13 00:55:17 Done.
366 user_prefs->RegisterListPref(prefs::kDnsPrefetchingStartupList,
367 PrefService::UNSYNCABLE_PREF);
368 user_prefs->RegisterListPref(prefs::kDnsPrefetchingHostReferralList,
369 PrefService::UNSYNCABLE_PREF);
370 }
371
372 void Predictor::ShutdownOnUIThread(PrefService* user_prefs) {
373 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
374
375 SaveStateForNextStartupAndTrim(user_prefs);
376
377 BrowserThread::PostTask(
378 BrowserThread::IO,
379 FROM_HERE,
380 base::Bind(&Predictor::Shutdown, base::Unretained(this)));
381 }
382
383 // ---------------------- End UI methods. -------------------------------------
384
385 // --------------------- Start IO methods. ------------------------------------
386
387 void Predictor::Shutdown() {
388 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
389 DCHECK(!shutdown_);
390 shutdown_ = true;
391
392 std::set<LookupRequest*>::iterator it;
393 for (it = pending_lookups_.begin(); it != pending_lookups_.end(); ++it)
394 delete *it;
395 }
396
397 void Predictor::DiscardAllResults() {
398 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
399 // Delete anything listed so far in this session that shows in about:dns.
400 referrers_.clear();
401
402
403 // Try to delete anything in our work queue.
404 while (!work_queue_.IsEmpty()) {
405 // Emulate processing cycle as though host was not found.
406 GURL url = work_queue_.Pop();
407 UrlInfo* info = &results_[url];
408 DCHECK(info->HasUrl(url));
409 info->SetAssignedState();
410 info->SetNoSuchNameState();
411 }
412 // Now every result_ is either resolved, or is being resolved
413 // (see LookupRequest).
414
415 // Step through result_, recording names of all hosts that can't be erased.
416 // We can't erase anything being worked on.
417 Results assignees;
418 for (Results::iterator it = results_.begin(); results_.end() != it; ++it) {
419 GURL url(it->first);
420 UrlInfo* info = &it->second;
421 DCHECK(info->HasUrl(url));
422 if (info->is_assigned()) {
423 info->SetPendingDeleteState();
424 assignees[url] = *info;
425 }
426 }
427 DCHECK(assignees.size() <= max_concurrent_dns_lookups_);
428 results_.clear();
429 // Put back in the names being worked on.
430 for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) {
431 DCHECK(it->second.is_marked_to_delete());
432 results_[it->first] = it->second;
433 }
434 }
435
436 // Overloaded Resolve() to take a vector of names.
437 void Predictor::ResolveList(const UrlList& urls,
438 UrlInfo::ResolutionMotivation motivation) {
439 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
440
441 for (UrlList::const_iterator it = urls.begin(); it < urls.end(); ++it) {
442 AppendToResolutionQueue(*it, motivation);
443 }
444 }
445
446 // Basic Resolve() takes an invidual name, and adds it
447 // to the queue.
448 void Predictor::Resolve(const GURL& url,
449 UrlInfo::ResolutionMotivation motivation) {
450 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
451 if (!url.has_host())
452 return;
453 AppendToResolutionQueue(url, motivation);
454 }
455
456 void Predictor::LearnFromNavigation(const GURL& referring_url,
457 const GURL& target_url) {
458 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
459 if (!predictor_enabled_)
460 return;
461 DCHECK_EQ(referring_url, Predictor::CanonicalizeUrl(referring_url));
462 DCHECK_NE(referring_url, GURL::EmptyGURL());
463 DCHECK_EQ(target_url, Predictor::CanonicalizeUrl(target_url));
464 DCHECK_NE(target_url, GURL::EmptyGURL());
465
466 referrers_[referring_url].SuggestHost(target_url);
467 // Possibly do some referrer trimming.
468 TrimReferrers();
307 } 469 }
308 470
309 // Provide sort order so all .com's are together, etc. 471 // Provide sort order so all .com's are together, etc.
310 struct RightToLeftStringSorter { 472 struct RightToLeftStringSorter {
311 bool operator()(const GURL& left, 473 bool operator()(const GURL& left,
312 const GURL& right) const { 474 const GURL& right) const {
313 return string_compare(left.host(), right.host()); 475 return string_compare(left.host(), right.host());
314 } 476 }
315 477
316 static bool string_compare(const std::string& left_host, 478 static bool string_compare(const std::string& left_host,
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
411 static_cast<int>(future_url->second.preresolution_count()), 573 static_cast<int>(future_url->second.preresolution_count()),
412 static_cast<double>(future_url->second.subresource_use_rate()), 574 static_cast<double>(future_url->second.subresource_use_rate()),
413 future_url->first.spec().c_str()); 575 future_url->first.spec().c_str());
414 } 576 }
415 } 577 }
416 output->append("</table>"); 578 output->append("</table>");
417 } 579 }
418 580
419 void Predictor::GetHtmlInfo(std::string* output) { 581 void Predictor::GetHtmlInfo(std::string* output) {
420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 582 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
583 if (initial_observer_.get())
584 initial_observer_->GetFirstResolutionsHtml(output);
585 // Show list of subresource predictions and stats.
586 GetHtmlReferrerLists(output);
587
421 // Local lists for calling UrlInfo 588 // Local lists for calling UrlInfo
422 UrlInfo::UrlInfoTable name_not_found; 589 UrlInfo::UrlInfoTable name_not_found;
423 UrlInfo::UrlInfoTable name_preresolved; 590 UrlInfo::UrlInfoTable name_preresolved;
424 591
425 // Get copies of all useful data. 592 // Get copies of all useful data.
426 typedef std::map<GURL, UrlInfo, RightToLeftStringSorter> SortedUrlInfo; 593 typedef std::map<GURL, UrlInfo, RightToLeftStringSorter> SortedUrlInfo;
427 SortedUrlInfo snapshot; 594 SortedUrlInfo snapshot;
428 // UrlInfo supports value semantics, so we can do a shallow copy. 595 // UrlInfo supports value semantics, so we can do a shallow copy.
429 for (Results::iterator it(results_.begin()); it != results_.end(); it++) 596 for (Results::iterator it(results_.begin()); it != results_.end(); it++)
430 snapshot[it->first] = it->second; 597 snapshot[it->first] = it->second;
(...skipping 15 matching lines...) Expand all
446 brief = true; 613 brief = true;
447 #endif // NDEBUG 614 #endif // NDEBUG
448 615
449 // Call for display of each table, along with title. 616 // Call for display of each table, along with title.
450 UrlInfo::GetHtmlTable(name_preresolved, 617 UrlInfo::GetHtmlTable(name_preresolved,
451 "Preresolution DNS records performed for ", brief, output); 618 "Preresolution DNS records performed for ", brief, output);
452 UrlInfo::GetHtmlTable(name_not_found, 619 UrlInfo::GetHtmlTable(name_not_found,
453 "Preresolving DNS records revealed non-existence for ", brief, output); 620 "Preresolving DNS records revealed non-existence for ", brief, output);
454 } 621 }
455 622
456 UrlInfo* Predictor::AppendToResolutionQueue( 623 void Predictor::TrimReferrersNow() {
457 const GURL& url, 624 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
625 // Just finish up work if an incremental trim is in progress.
626 if (urls_being_trimmed_.empty())
627 LoadUrlsForTrimming();
628 IncrementalTrimReferrers(true); // Do everything now.
629 }
630
631 void Predictor::SerializeReferrers(base::ListValue* referral_list) {
632 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
633 referral_list->Clear();
634 referral_list->Append(new base::FundamentalValue(PREDICTOR_REFERRER_VERSION));
635 for (Referrers::const_iterator it = referrers_.begin();
636 it != referrers_.end(); ++it) {
637 // Serialize the list of subresource names.
638 Value* subresource_list(it->second.Serialize());
639
640 // Create a list for each referer.
641 ListValue* motivator(new ListValue);
642 motivator->Append(new StringValue(it->first.spec()));
643 motivator->Append(subresource_list);
644
645 referral_list->Append(motivator);
646 }
647 }
648
649 void Predictor::DeserializeReferrers(const base::ListValue& referral_list) {
650 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
651 int format_version = -1;
652 if (referral_list.GetSize() > 0 &&
653 referral_list.GetInteger(0, &format_version) &&
654 format_version == PREDICTOR_REFERRER_VERSION) {
655 for (size_t i = 1; i < referral_list.GetSize(); ++i) {
656 base::ListValue* motivator;
657 if (!referral_list.GetList(i, &motivator)) {
658 NOTREACHED();
659 return;
660 }
661 std::string motivating_url_spec;
662 if (!motivator->GetString(0, &motivating_url_spec)) {
663 NOTREACHED();
664 return;
665 }
666
667 Value* subresource_list;
668 if (!motivator->Get(1, &subresource_list)) {
669 NOTREACHED();
670 return;
671 }
672
673 referrers_[GURL(motivating_url_spec)].Deserialize(*subresource_list);
674 }
675 }
676 }
677
678 void Predictor::DeserializeReferrersThenDelete(
679 base::ListValue* referral_list) {
680 DeserializeReferrers(*referral_list);
681 delete referral_list;
682 }
683
684 void Predictor::DiscardInitialNavigationHistory() {
685 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
686 if (initial_observer_.get())
687 initial_observer_->DiscardInitialNavigationHistory();
688 }
689
690 void Predictor::FinalizeInitializationOnIOThread(
691 const UrlList& startup_urls,
692 base::ListValue* referral_list,
693 IOThread* io_thread) {
694 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
695
696 host_resolver_ = io_thread->globals()->host_resolver.get();
697
698 // ScopedrunnableMethodFactory instances need to be created and destroyed
699 // on the same thread. The predictor lives on the IO thread and will die
700 // from there so now that we're on the IO thread we need to properly
701 // initialize the ScopedrunnableMethodFactory.
702 trim_task_factory_.reset(new ScopedRunnableMethodFactory<Predictor>(this));
703
704 // Prefetch these hostnames on startup.
705 DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED);
706 DeserializeReferrersThenDelete(referral_list);
707 }
708
709 //-----------------------------------------------------------------------------
710 // This section intermingles prefetch results with actual browser HTTP
711 // network activity. It supports calculating of the benefit of a prefetch, as
712 // well as recording what prefetched hostname resolutions might be potentially
713 // helpful during the next chrome-startup.
714 //-----------------------------------------------------------------------------
715
716 void Predictor::LearnAboutInitialNavigation(const GURL& url) {
717 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
718 if (!predictor_enabled_ || NULL == initial_observer_.get() )
719 return;
720 initial_observer_->Append(url, this);
721 }
722
723 // This API is only used in the browser process.
724 // It is called from an IPC message originating in the renderer. It currently
725 // includes both Page-Scan, and Link-Hover prefetching.
726 // TODO(jar): Separate out link-hover prefetching, and page-scan results.
727 void Predictor::DnsPrefetchList(const NameList& hostnames) {
728 // TODO(jar): Push GURL transport further back into renderer, but this will
729 // require a Webkit change in the observer :-/.
730 UrlList urls;
731 for (NameList::const_iterator it = hostnames.begin();
732 it < hostnames.end();
733 ++it) {
734 urls.push_back(GURL("http://" + *it + ":80"));
735 }
736
737 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
738 DnsPrefetchMotivatedList(urls, UrlInfo::PAGE_SCAN_MOTIVATED);
739 }
740
741 void Predictor::DnsPrefetchMotivatedList(
742 const UrlList& urls,
458 UrlInfo::ResolutionMotivation motivation) { 743 UrlInfo::ResolutionMotivation motivation) {
459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 744 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
460 DCHECK(url.has_host()); 745 BrowserThread::CurrentlyOn(BrowserThread::IO));
461 746 if (!predictor_enabled_)
462 if (shutdown_) 747 return;
463 return NULL; 748
464 749 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
465 UrlInfo* info = &results_[url]; 750 ResolveList(urls, motivation);
466 info->SetUrl(url); // Initialize or DCHECK. 751 } else {
467 // TODO(jar): I need to discard names that have long since expired. 752 BrowserThread::PostTask(
468 // Currently we only add to the domain map :-/ 753 BrowserThread::IO,
469 754 FROM_HERE,
470 DCHECK(info->HasUrl(url)); 755 base::Bind(&Predictor::ResolveList, base::Unretained(this),
471 756 urls, motivation));
472 if (!info->NeedsDnsUpdate()) { 757 }
473 info->DLogResultsStats("DNS PrefetchNotUpdated"); 758 }
474 return NULL; 759 //-----------------------------------------------------------------------------
475 } 760 // Functions to handle saving of hostnames from one session to the next, to
476 761 // expedite startup times.
477 info->SetQueuedState(motivation); 762
478 work_queue_.Push(url, motivation); 763 static void SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread(
479 StartSomeQueuedResolutions(); 764 base::ListValue* startup_list,
480 return info; 765 base::ListValue* referral_list,
481 } 766 base::WaitableEvent* completion,
482 767 Predictor* predictor) {
483 void Predictor::StartSomeQueuedResolutions() { 768 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 769
485 770 if (NULL == predictor) {
486 while (!work_queue_.IsEmpty() && 771 completion->Signal();
487 pending_lookups_.size() < max_concurrent_dns_lookups_) { 772 return;
488 const GURL url(work_queue_.Pop()); 773 }
489 UrlInfo* info = &results_[url]; 774 predictor->SaveDnsPrefetchStateForNextStartupAndTrim(
490 DCHECK(info->HasUrl(url)); 775 startup_list, referral_list, completion);
491 info->SetAssignedState(); 776 }
492 777
493 if (CongestionControlPerformed(info)) { 778 void Predictor::SaveStateForNextStartupAndTrim(PrefService* prefs) {
494 DCHECK(work_queue_.IsEmpty()); 779 if (!predictor_enabled_)
495 return; 780 return;
781
782 base::WaitableEvent completion(true, false);
783
784 ListPrefUpdate update_startup_list(prefs, prefs::kDnsPrefetchingStartupList);
785 ListPrefUpdate update_referral_list(prefs,
786 prefs::kDnsPrefetchingHostReferralList);
787 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
788 SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread(
789 update_startup_list.Get(),
790 update_referral_list.Get(),
791 &completion,
792 this);
793 } else {
794 bool posted = BrowserThread::PostTask(
795 BrowserThread::IO,
796 FROM_HERE,
797 base::Bind(
798 SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread,
799 update_startup_list.Get(),
800 update_referral_list.Get(),
801 &completion,
802 this));
803
804 // TODO(jar): Synchronous waiting for the IO thread is a potential source
805 // to deadlocks and should be investigated. See http://crbug.com/78451.
806 DCHECK(posted);
807 if (posted)
808 completion.Wait();
809 }
810 }
811
812 void Predictor::SaveDnsPrefetchStateForNextStartupAndTrim(
813 base::ListValue* startup_list,
814 base::ListValue* referral_list,
815 base::WaitableEvent* completion) {
816 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
817 if (initial_observer_.get())
818 initial_observer_->GetInitialDnsResolutionList(startup_list);
819
820 // Do at least one trim at shutdown, in case the user wasn't running long
821 // enough to do any regular trimming of referrers.
822 TrimReferrersNow();
823 SerializeReferrers(referral_list);
824
825 completion->Signal();
826 }
827
828 void Predictor::EnablePredictor(bool enable) {
829 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
830 BrowserThread::CurrentlyOn(BrowserThread::IO));
831
832 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
833 EnablePredictorOnIOThread(enable);
834 } else {
835 BrowserThread::PostTask(
836 BrowserThread::IO,
837 FROM_HERE,
838 base::Bind(&Predictor::EnablePredictorOnIOThread,
839 base::Unretained(this), enable));
840 }
841 }
842
843 void Predictor::EnablePredictorOnIOThread(bool enable) {
844 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
845 predictor_enabled_ = enable;
846 }
847
848 enum SubresourceValue {
849 PRECONNECTION,
850 PRERESOLUTION,
851 TOO_NEW,
852 SUBRESOURCE_VALUE_MAX
853 };
854
855 void Predictor::PrepareFrameSubresources(const GURL& url) {
856 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
857 DCHECK_EQ(url.GetWithEmptyPath(), url);
858 Referrers::iterator it = referrers_.find(url);
859 if (referrers_.end() == it) {
860 // Only when we don't know anything about this url, make 2 connections
861 // available. We could do this completely via learning (by prepopulating
862 // the referrer_ list with this expected value), but it would swell the
863 // size of the list with all the "Leaf" nodes in the tree (nodes that don't
864 // load any subresources). If we learn about this resource, we will instead
865 // provide a more carefully estimated preconnection count.
866 if (preconnect_enabled_)
867 PreconnectOnIOThread(url, UrlInfo::SELF_REFERAL_MOTIVATED, 2);
868 return;
869 }
870
871 Referrer* referrer = &(it->second);
872 referrer->IncrementUseCount();
873 const UrlInfo::ResolutionMotivation motivation =
874 UrlInfo::LEARNED_REFERAL_MOTIVATED;
875 for (Referrer::iterator future_url = referrer->begin();
876 future_url != referrer->end(); ++future_url) {
877 SubresourceValue evalution(TOO_NEW);
878 double connection_expectation = future_url->second.subresource_use_rate();
879 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.PreconnectSubresourceExpectation",
880 static_cast<int>(connection_expectation * 100),
881 10, 5000, 50);
882 future_url->second.ReferrerWasObserved();
883 if (preconnect_enabled_ &&
884 connection_expectation > kPreconnectWorthyExpectedValue) {
885 evalution = PRECONNECTION;
886 future_url->second.IncrementPreconnectionCount();
887 int count = static_cast<int>(std::ceil(connection_expectation));
888 if (url.host() == future_url->first.host())
889 ++count;
890 PreconnectOnIOThread(future_url->first, motivation, count);
891 } else if (connection_expectation > kDNSPreresolutionWorthyExpectedValue) {
892 evalution = PRERESOLUTION;
893 future_url->second.preresolution_increment();
894 UrlInfo* queued_info = AppendToResolutionQueue(future_url->first,
895 motivation);
896 if (queued_info)
897 queued_info->SetReferringHostname(url);
496 } 898 }
497 899 UMA_HISTOGRAM_ENUMERATION("Net.PreconnectSubresourceEval", evalution,
498 LookupRequest* request = new LookupRequest(this, host_resolver_, url); 900 SUBRESOURCE_VALUE_MAX);
499 int status = request->Start(); 901 }
500 if (status == net::ERR_IO_PENDING) {
501 // Will complete asynchronously.
502 pending_lookups_.insert(request);
503 peak_pending_lookups_ = std::max(peak_pending_lookups_,
504 pending_lookups_.size());
505 } else {
506 // Completed synchronously (was already cached by HostResolver), or else
507 // there was (equivalently) some network error that prevents us from
508 // finding the name. Status net::OK means it was "found."
509 LookupFinished(request, url, status == net::OK);
510 delete request;
511 }
512 }
513 }
514
515 bool Predictor::CongestionControlPerformed(UrlInfo* info) {
516 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
517 // Note: queue_duration is ONLY valid after we go to assigned state.
518 if (info->queue_duration() < max_dns_queue_delay_)
519 return false;
520 // We need to discard all entries in our queue, as we're keeping them waiting
521 // too long. By doing this, we'll have a chance to quickly service urgent
522 // resolutions, and not have a bogged down system.
523 while (true) {
524 info->RemoveFromQueue();
525 if (work_queue_.IsEmpty())
526 break;
527 info = &results_[work_queue_.Pop()];
528 info->SetAssignedState();
529 }
530 return true;
531 } 902 }
532 903
533 void Predictor::OnLookupFinished(LookupRequest* request, const GURL& url, 904 void Predictor::OnLookupFinished(LookupRequest* request, const GURL& url,
534 bool found) { 905 bool found) {
535 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 906 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
536 907
537 LookupFinished(request, url, found); 908 LookupFinished(request, url, found);
538 pending_lookups_.erase(request); 909 pending_lookups_.erase(request);
539 delete request; 910 delete request;
540 911
541 StartSomeQueuedResolutions(); 912 StartSomeQueuedResolutions();
542 } 913 }
543 914
544 void Predictor::LookupFinished(LookupRequest* request, const GURL& url, 915 void Predictor::LookupFinished(LookupRequest* request, const GURL& url,
545 bool found) { 916 bool found) {
546 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 917 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
547 UrlInfo* info = &results_[url]; 918 UrlInfo* info = &results_[url];
548 DCHECK(info->HasUrl(url)); 919 DCHECK(info->HasUrl(url));
549 if (info->is_marked_to_delete()) { 920 if (info->is_marked_to_delete()) {
550 results_.erase(url); 921 results_.erase(url);
551 } else { 922 } else {
552 if (found) 923 if (found)
553 info->SetFoundState(); 924 info->SetFoundState();
554 else 925 else
555 info->SetNoSuchNameState(); 926 info->SetNoSuchNameState();
556 } 927 }
557 } 928 }
558 929
559 void Predictor::DiscardAllResults() { 930 UrlInfo* Predictor::AppendToResolutionQueue(
931 const GURL& url,
932 UrlInfo::ResolutionMotivation motivation) {
560 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 933 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
561 // Delete anything listed so far in this session that shows in about:dns. 934 DCHECK(url.has_host());
562 referrers_.clear();
563 935
936 if (shutdown_)
937 return NULL;
564 938
565 // Try to delete anything in our work queue. 939 UrlInfo* info = &results_[url];
566 while (!work_queue_.IsEmpty()) { 940 info->SetUrl(url); // Initialize or DCHECK.
567 // Emulate processing cycle as though host was not found. 941 // TODO(jar): I need to discard names that have long since expired.
568 GURL url = work_queue_.Pop(); 942 // Currently we only add to the domain map :-/
943
944 DCHECK(info->HasUrl(url));
945
946 if (!info->NeedsDnsUpdate()) {
947 info->DLogResultsStats("DNS PrefetchNotUpdated");
948 return NULL;
949 }
950
951 info->SetQueuedState(motivation);
952 work_queue_.Push(url, motivation);
953 StartSomeQueuedResolutions();
954 return info;
955 }
956
957 bool Predictor::CongestionControlPerformed(UrlInfo* info) {
958 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
959 // Note: queue_duration is ONLY valid after we go to assigned state.
960 if (info->queue_duration() < max_dns_queue_delay_)
961 return false;
962 // We need to discard all entries in our queue, as we're keeping them waiting
963 // too long. By doing this, we'll have a chance to quickly service urgent
964 // resolutions, and not have a bogged down system.
965 while (true) {
966 info->RemoveFromQueue();
967 if (work_queue_.IsEmpty())
968 break;
969 info = &results_[work_queue_.Pop()];
970 info->SetAssignedState();
971 }
972 return true;
973 }
974
975 void Predictor::StartSomeQueuedResolutions() {
976 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
977
978 while (!work_queue_.IsEmpty() &&
979 pending_lookups_.size() < max_concurrent_dns_lookups_) {
980 const GURL url(work_queue_.Pop());
569 UrlInfo* info = &results_[url]; 981 UrlInfo* info = &results_[url];
570 DCHECK(info->HasUrl(url)); 982 DCHECK(info->HasUrl(url));
571 info->SetAssignedState(); 983 info->SetAssignedState();
572 info->SetNoSuchNameState();
573 }
574 // Now every result_ is either resolved, or is being resolved
575 // (see LookupRequest).
576 984
577 // Step through result_, recording names of all hosts that can't be erased. 985 if (CongestionControlPerformed(info)) {
578 // We can't erase anything being worked on. 986 DCHECK(work_queue_.IsEmpty());
579 Results assignees; 987 return;
580 for (Results::iterator it = results_.begin(); results_.end() != it; ++it) {
581 GURL url(it->first);
582 UrlInfo* info = &it->second;
583 DCHECK(info->HasUrl(url));
584 if (info->is_assigned()) {
585 info->SetPendingDeleteState();
586 assignees[url] = *info;
587 } 988 }
588 }
589 DCHECK(assignees.size() <= max_concurrent_dns_lookups_);
590 results_.clear();
591 // Put back in the names being worked on.
592 for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) {
593 DCHECK(it->second.is_marked_to_delete());
594 results_[it->first] = it->second;
595 }
596 }
597 989
598 void Predictor::TrimReferrersNow() { 990 LookupRequest* request = new LookupRequest(this, host_resolver_, url);
599 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 991 int status = request->Start();
600 // Just finish up work if an incremental trim is in progress. 992 if (status == net::ERR_IO_PENDING) {
601 if (urls_being_trimmed_.empty()) 993 // Will complete asynchronously.
602 LoadUrlsForTrimming(); 994 pending_lookups_.insert(request);
603 IncrementalTrimReferrers(true); // Do everything now. 995 peak_pending_lookups_ = std::max(peak_pending_lookups_,
604 } 996 pending_lookups_.size());
605 997 } else {
606 void Predictor::SerializeReferrers(ListValue* referral_list) { 998 // Completed synchronously (was already cached by HostResolver), or else
607 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 999 // there was (equivalently) some network error that prevents us from
608 referral_list->Clear(); 1000 // finding the name. Status net::OK means it was "found."
609 referral_list->Append(new base::FundamentalValue(PREDICTOR_REFERRER_VERSION)); 1001 LookupFinished(request, url, status == net::OK);
610 for (Referrers::const_iterator it = referrers_.begin(); 1002 delete request;
611 it != referrers_.end(); ++it) {
612 // Serialize the list of subresource names.
613 Value* subresource_list(it->second.Serialize());
614
615 // Create a list for each referer.
616 ListValue* motivator(new ListValue);
617 motivator->Append(new StringValue(it->first.spec()));
618 motivator->Append(subresource_list);
619
620 referral_list->Append(motivator);
621 }
622 }
623
624 void Predictor::DeserializeReferrers(const ListValue& referral_list) {
625 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
626 int format_version = -1;
627 if (referral_list.GetSize() > 0 &&
628 referral_list.GetInteger(0, &format_version) &&
629 format_version == PREDICTOR_REFERRER_VERSION) {
630 for (size_t i = 1; i < referral_list.GetSize(); ++i) {
631 ListValue* motivator;
632 if (!referral_list.GetList(i, &motivator)) {
633 NOTREACHED();
634 return;
635 }
636 std::string motivating_url_spec;
637 if (!motivator->GetString(0, &motivating_url_spec)) {
638 NOTREACHED();
639 return;
640 }
641
642 Value* subresource_list;
643 if (!motivator->Get(1, &subresource_list)) {
644 NOTREACHED();
645 return;
646 }
647
648 referrers_[GURL(motivating_url_spec)].Deserialize(*subresource_list);
649 } 1003 }
650 } 1004 }
651 } 1005 }
652 1006
653 void Predictor::TrimReferrers() { 1007 void Predictor::TrimReferrers() {
654 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 1008 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
655 if (!urls_being_trimmed_.empty()) 1009 if (!urls_being_trimmed_.empty())
656 return; // There is incremental trimming in progress already. 1010 return; // There is incremental trimming in progress already.
657 1011
658 // Check to see if it is time to trim yet. 1012 // Check to see if it is time to trim yet.
(...skipping 12 matching lines...) Expand all
671 it != referrers_.end(); ++it) 1025 it != referrers_.end(); ++it)
672 urls_being_trimmed_.push_back(it->first); 1026 urls_being_trimmed_.push_back(it->first);
673 UMA_HISTOGRAM_COUNTS("Net.PredictionTrimSize", urls_being_trimmed_.size()); 1027 UMA_HISTOGRAM_COUNTS("Net.PredictionTrimSize", urls_being_trimmed_.size());
674 } 1028 }
675 1029
676 void Predictor::PostIncrementalTrimTask() { 1030 void Predictor::PostIncrementalTrimTask() {
677 if (urls_being_trimmed_.empty()) 1031 if (urls_being_trimmed_.empty())
678 return; 1032 return;
679 MessageLoop::current()->PostDelayedTask( 1033 MessageLoop::current()->PostDelayedTask(
680 FROM_HERE, 1034 FROM_HERE,
681 trim_task_factory_.NewRunnableMethod(&Predictor::IncrementalTrimReferrers, 1035 trim_task_factory_->NewRunnableMethod(
682 false), 1036 &Predictor::IncrementalTrimReferrers, false),
683 kDurationBetweenTrimmingIncrements.InMilliseconds()); 1037 kDurationBetweenTrimmingIncrements.InMilliseconds());
684 } 1038 }
685 1039
686 void Predictor::IncrementalTrimReferrers(bool trim_all_now) { 1040 void Predictor::IncrementalTrimReferrers(bool trim_all_now) {
687 size_t trim_count = urls_being_trimmed_.size(); 1041 size_t trim_count = urls_being_trimmed_.size();
688 if (!trim_all_now) 1042 if (!trim_all_now)
689 trim_count = std::min(trim_count, kUrlsTrimmedPerIncrement); 1043 trim_count = std::min(trim_count, kUrlsTrimmedPerIncrement);
690 while (trim_count-- != 0) { 1044 while (trim_count-- != 0) {
691 Referrers::iterator it = referrers_.find(urls_being_trimmed_.back()); 1045 Referrers::iterator it = referrers_.find(urls_being_trimmed_.back());
692 urls_being_trimmed_.pop_back(); 1046 urls_being_trimmed_.pop_back();
693 if (it == referrers_.end()) 1047 if (it == referrers_.end())
694 continue; // Defensive code: It got trimmed away already. 1048 continue; // Defensive code: It got trimmed away already.
695 if (!it->second.Trim(kReferrerTrimRatio, kDiscardableExpectedValue)) 1049 if (!it->second.Trim(kReferrerTrimRatio, kDiscardableExpectedValue))
696 referrers_.erase(it); 1050 referrers_.erase(it);
697 } 1051 }
698 PostIncrementalTrimTask(); 1052 PostIncrementalTrimTask();
699 } 1053 }
700 1054
701 //------------------------------------------------------------------------------ 1055 // ---------------------- End UI methods. -------------------------------------
1056
1057 //-----------------------------------------------------------------------------
702 1058
703 Predictor::HostNameQueue::HostNameQueue() { 1059 Predictor::HostNameQueue::HostNameQueue() {
704 } 1060 }
705 1061
706 Predictor::HostNameQueue::~HostNameQueue() { 1062 Predictor::HostNameQueue::~HostNameQueue() {
707 } 1063 }
708 1064
709 void Predictor::HostNameQueue::Push(const GURL& url, 1065 void Predictor::HostNameQueue::Push(const GURL& url,
710 UrlInfo::ResolutionMotivation motivation) { 1066 UrlInfo::ResolutionMotivation motivation) {
711 switch (motivation) { 1067 switch (motivation) {
(...skipping 15 matching lines...) Expand all
727 1083
728 GURL Predictor::HostNameQueue::Pop() { 1084 GURL Predictor::HostNameQueue::Pop() {
729 DCHECK(!IsEmpty()); 1085 DCHECK(!IsEmpty());
730 std::queue<GURL> *queue(rush_queue_.empty() ? &background_queue_ 1086 std::queue<GURL> *queue(rush_queue_.empty() ? &background_queue_
731 : &rush_queue_); 1087 : &rush_queue_);
732 GURL url(queue->front()); 1088 GURL url(queue->front());
733 queue->pop(); 1089 queue->pop();
734 return url; 1090 return url;
735 } 1091 }
736 1092
737 void Predictor::DeserializeReferrersThenDelete(ListValue* referral_list) { 1093 //-----------------------------------------------------------------------------
738 DeserializeReferrers(*referral_list); 1094 // Member definitions for InitialObserver class.
739 delete referral_list; 1095
1096 void Predictor::InitialObserver::Append(const GURL& url,
1097 Predictor* predictor) {
1098 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1099
1100 // TODO(rlp): Do we really need the predictor check here?
1101 if (NULL == predictor)
1102 return;
1103 if (kStartupResolutionCount <= first_navigations_.size())
1104 return;
1105
1106 DCHECK(url.SchemeIs("http") || url.SchemeIs("https"));
1107 DCHECK_EQ(url, Predictor::CanonicalizeUrl(url));
1108 if (first_navigations_.find(url) == first_navigations_.end())
1109 first_navigations_[url] = base::TimeTicks::Now();
740 } 1110 }
741 1111
1112 void Predictor::InitialObserver::GetInitialDnsResolutionList(
1113 base::ListValue* startup_list) {
1114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1115 DCHECK(startup_list);
1116 startup_list->Clear();
1117 DCHECK_EQ(0u, startup_list->GetSize());
1118 startup_list->Append(
1119 new base::FundamentalValue(kPredictorStartupFormatVersion));
1120 for (FirstNavigations::iterator it = first_navigations_.begin();
1121 it != first_navigations_.end();
1122 ++it) {
1123 DCHECK(it->first == Predictor::CanonicalizeUrl(it->first));
1124 startup_list->Append(new StringValue(it->first.spec()));
1125 }
1126 }
742 1127
743 //------------------------------------------------------------------------------ 1128 void Predictor::InitialObserver::GetFirstResolutionsHtml(
1129 std::string* output) {
1130 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1131
1132 UrlInfo::UrlInfoTable resolution_list;
1133 {
1134 for (FirstNavigations::iterator it(first_navigations_.begin());
1135 it != first_navigations_.end();
1136 it++) {
1137 UrlInfo info;
1138 info.SetUrl(it->first);
1139 info.set_time(it->second);
1140 resolution_list.push_back(info);
1141 }
1142 }
1143 UrlInfo::GetHtmlTable(resolution_list,
1144 "Future startups will prefetch DNS records for ", false, output);
1145 }
1146
1147 //-----------------------------------------------------------------------------
744 // Helper functions 1148 // Helper functions
745 //------------------------------------------------------------------------------ 1149 //-----------------------------------------------------------------------------
746 1150
747 // static 1151 // static
748 GURL Predictor::CanonicalizeUrl(const GURL& url) { 1152 GURL Predictor::CanonicalizeUrl(const GURL& url) {
749 if (!url.has_host()) 1153 if (!url.has_host())
750 return GURL::EmptyGURL(); 1154 return GURL::EmptyGURL();
751 1155
752 std::string scheme; 1156 std::string scheme;
753 if (url.has_scheme()) { 1157 if (url.has_scheme()) {
754 scheme = url.scheme(); 1158 scheme = url.scheme();
755 if (scheme != "http" && scheme != "https") 1159 if (scheme != "http" && scheme != "https")
756 return GURL::EmptyGURL(); 1160 return GURL::EmptyGURL();
757 if (url.has_port()) 1161 if (url.has_port())
758 return url.GetWithEmptyPath(); 1162 return url.GetWithEmptyPath();
759 } else { 1163 } else {
760 scheme = "http"; 1164 scheme = "http";
761 } 1165 }
762 1166
763 // If we omit a port, it will default to 80 or 443 as appropriate. 1167 // If we omit a port, it will default to 80 or 443 as appropriate.
764 std::string colon_plus_port; 1168 std::string colon_plus_port;
765 if (url.has_port()) 1169 if (url.has_port())
766 colon_plus_port = ":" + url.port(); 1170 colon_plus_port = ":" + url.port();
767 1171
768 return GURL(scheme + "://" + url.host() + colon_plus_port); 1172 return GURL(scheme + "://" + url.host() + colon_plus_port);
769 } 1173 }
770 1174
771
772 } // namespace chrome_browser_net 1175 } // namespace chrome_browser_net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698