OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |