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