diff options
| author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
|---|---|---|
| committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
| commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
| tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/net | |
| parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
| download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 | |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/net')
| -rw-r--r-- | chrome/browser/net/dns_global.cc | 463 | ||||
| -rw-r--r-- | chrome/browser/net/dns_global.h | 80 | ||||
| -rw-r--r-- | chrome/browser/net/dns_host_info.cc | 361 | ||||
| -rw-r--r-- | chrome/browser/net/dns_host_info.h | 174 | ||||
| -rw-r--r-- | chrome/browser/net/dns_host_info_unittest.cc | 114 | ||||
| -rw-r--r-- | chrome/browser/net/dns_master.cc | 454 | ||||
| -rw-r--r-- | chrome/browser/net/dns_master.h | 201 | ||||
| -rw-r--r-- | chrome/browser/net/dns_master_unittest.cc | 451 | ||||
| -rw-r--r-- | chrome/browser/net/dns_slave.cc | 115 | ||||
| -rw-r--r-- | chrome/browser/net/dns_slave.h | 96 |
10 files changed, 2509 insertions, 0 deletions
diff --git a/chrome/browser/net/dns_global.cc b/chrome/browser/net/dns_global.cc new file mode 100644 index 0000000..114f548 --- /dev/null +++ b/chrome/browser/net/dns_global.cc @@ -0,0 +1,463 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/net/dns_global.h" + +#include <map> +#include <string> + +#include "base/stats_counters.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chrome/browser/net/dns_host_info.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/session_startup_pref.h" +#include "chrome/common/notification_types.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "googleurl/src/gurl.h" +#include "net/base/dns_resolution_observer.h" + +namespace chrome_browser_net { + +static void DiscardAllPrefetchState(); + +//------------------------------------------------------------------------------ +// This section contains all the globally accessable API entry points for the +// DNS Prefetching feature. +//------------------------------------------------------------------------------ + +// Status of prefetch feature, controlling whether any prefetching is done. +static bool dns_prefetch_enabled = true; + +// Cached inverted copy of the off_the_record pref. +static bool on_the_record_switch = true; + +// Enable/disable Dns prefetch activity (either via command line, or via pref). +void EnableDnsPrefetch(bool enable) { + dns_prefetch_enabled = enable; +} + +void OnTheRecord(bool enable) { + if (on_the_record_switch == enable) + return; + on_the_record_switch = enable; + if (on_the_record_switch) + DiscardAllPrefetchState(); // Destroy all evidence of our OTR session. +} + +void RegisterPrefs(PrefService* local_state) { + local_state->RegisterListPref(prefs::kDnsStartupPrefetchList); +} + +void RegisterUserPrefs(PrefService* user_prefs) { + user_prefs->RegisterBooleanPref(prefs::kDnsPrefetchingEnabled, true); +} + +// When enabled, we use the following instance to service all requests in the +// browser process. +static DnsMaster* dns_master; + +// This API is only used in the browser process. +void DnsPrefetchList(const NameList& hostnames) { + if (!dns_prefetch_enabled) + return; + DCHECK(NULL != dns_master); + if (NULL != dns_master) + dns_master->ResolveList(hostnames); +} + +// This API is used by the autocomplete popup box (wher URLs are typed). +void DnsPrefetchUrlString(const url_canon::UTF16String& url_string) { + if (!dns_prefetch_enabled || NULL == dns_master) + return; + GURL gurl(url_string); + if (gurl.is_valid()) { + DnsPrefetch(gurl.host()); + } +} + +// This API currently used after translating a url_string. +void DnsPrefetch(const std::string& hostname) { + if (!dns_prefetch_enabled || NULL == dns_master || !hostname.size()) + return; + dns_master->Resolve(hostname); +} + +//------------------------------------------------------------------------------ +// This section intermingles prefetch results with actual browser HTTP +// network activity. It supports calculating of the benefit of a prefetch, as +// well as recording what prefetched hostname resolutions might be potentially +// helpful during the next chrome-startup. +//------------------------------------------------------------------------------ + +// This function determines if there was a saving by prefetching the hostname +// for which the host_info is supplied. +static bool AcruePrefetchBenefits(DnsHostInfo* host_info) { + if (!dns_prefetch_enabled || NULL == dns_master) + return false; + return dns_master->AcruePrefetchBenefits(host_info); +} + +// The observer class needs to connect starts and finishes of HTTP network +// resolutions. We use the following type for that map. +typedef std::map<void*, DnsHostInfo> ObservedResolutionMap; + +// There will only be one instance ever created of the following Observer +// class. As a result, we get away with using static members for data local +// to that instance (to better comply with a google style guide exemption). +class PrefetchObserver : public net::DnsResolutionObserver { + public: + PrefetchObserver(); + ~PrefetchObserver(); + + virtual void OnStartResolution(const std::string& name, void* context); + virtual void OnFinishResolutionWithStatus(bool was_resolved, void* context); + + static void DnsGetFirstResolutionsHtml(std::string* output); + static void SaveStartupListAsPref(PrefService* local_state); + + private: + static void StartupListAppend(const DnsHostInfo& host_info); + + // We avoid using member variables to better comply with the style guide. + // We had permission to instantiate only a very minimal class as a global + // data item, so we avoid putting members in that class. + // There is really only one instance of this class, and it would have been + // much simpler to use member variables than these static members. + static Lock* lock; + // Map of pending resolutions seen by observer. + static ObservedResolutionMap* resolutions; + // List of the first N hostname resolutions observed in this run. + static Results* first_resolutions; + // The number of hostnames we'll save for prefetching at next startup. + static const size_t kStartupResolutionCount = 10; +}; + +//------------------------------------------------------------------------------ +// Member definitions for above Observer class. + +PrefetchObserver::PrefetchObserver() { + DCHECK(!lock && !resolutions && !first_resolutions); + lock = new Lock; + resolutions = new ObservedResolutionMap; + first_resolutions = new Results; +} + +PrefetchObserver::~PrefetchObserver() { + DCHECK(lock && resolutions && first_resolutions); + delete first_resolutions; + first_resolutions = NULL; + delete resolutions; + resolutions = NULL; + delete lock; + lock = NULL; +} + +void PrefetchObserver::OnStartResolution(const std::string& name, + void* context) { + DCHECK_NE(0, name.length()); + DnsHostInfo host_info; + host_info.SetHostname(name); + host_info.SetStartedState(); + + AutoLock auto_lock(*lock); + (*resolutions)[context] = host_info; +} + +void PrefetchObserver::OnFinishResolutionWithStatus(bool was_resolved, + void* context) { + DnsHostInfo host_info; + size_t startup_count; + { + AutoLock auto_lock(*lock); + ObservedResolutionMap::iterator it = resolutions->find(context); + if (resolutions->end() == it) { + return; + } + host_info = it->second; + resolutions->erase(it); + startup_count = first_resolutions->size(); + } + host_info.SetFinishedState(was_resolved); // Get timing info + AcruePrefetchBenefits(&host_info); // Update prefetch benefit (if any). + if (kStartupResolutionCount <= startup_count || !was_resolved) + return; + StartupListAppend(host_info); +} + +// static +void PrefetchObserver::StartupListAppend(const DnsHostInfo& host_info) { + if (!on_the_record_switch || NULL == dns_master) + return; + AutoLock auto_lock(*lock); + if (kStartupResolutionCount <= first_resolutions->size()) + return; // Someone just added the last item. + std::string host_name = host_info.hostname(); + if (first_resolutions->find(host_name) != first_resolutions->end()) + return; // We already have this hostname listed. + (*first_resolutions)[host_name] = host_info; +} + +// static +void PrefetchObserver::SaveStartupListAsPref(PrefService* local_state) { + ListValue* startup_list = + local_state->GetMutableList(prefs::kDnsStartupPrefetchList); + + DCHECK(startup_list); + if (!startup_list) + return; + startup_list->Clear(); + DCHECK(startup_list->GetSize() == 0); + AutoLock auto_lock(*lock); + for (Results::iterator it = first_resolutions->begin(); + it != first_resolutions->end(); + it++) { + const std::wstring hostname = ASCIIToWide(it->first); + startup_list->Append(Value::CreateStringValue(hostname)); + } +} + +// static +void PrefetchObserver::DnsGetFirstResolutionsHtml(std::string* output) { + DnsHostInfo::DnsInfoTable resolution_list; + { + AutoLock auto_lock(*lock); + for (Results::iterator it(first_resolutions->begin()); + it != first_resolutions->end(); + it++) { + resolution_list.push_back(it->second); + } + } + DnsHostInfo::GetHtmlTable(resolution_list, + "Future startups will prefetch DNS records for ", false, output); +} + +// static +Lock* PrefetchObserver::lock = NULL; +// static +ObservedResolutionMap* PrefetchObserver::resolutions = NULL; +// static +Results* PrefetchObserver::first_resolutions = NULL; + +//------------------------------------------------------------------------------ +// Support observer to detect opening and closing of OffTheRecord windows. + +class OffTheRecordObserver : public NotificationObserver { + public: + OffTheRecordObserver() : lock_(), count_off_the_record_windows_(0) { } + + ~OffTheRecordObserver() { } + + // Register as an observer, and rely on the NotificationSystem shutdown + // to unregister us (at the last possible moment). + void Register() { + NotificationService* service = NotificationService::current(); + // TODO(tc): These notification observers are never removed. + service->AddObserver(this, NOTIFY_BROWSER_CLOSED, + NotificationService::AllSources()); + service->AddObserver(this, NOTIFY_BROWSER_OPENED, + NotificationService::AllSources()); + } + + void Observe(NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + switch (type) { + case NOTIFY_BROWSER_OPENED: + if (!Source<Browser>(source)->profile()->IsOffTheRecord()) + break; + { + AutoLock lock(lock_); + ++count_off_the_record_windows_; + } + OnTheRecord(false); + break; + + case NOTIFY_BROWSER_CLOSED: + if (!Source<Browser>(source)->profile()->IsOffTheRecord()) + break; // Ignore ordinary windows. + { + AutoLock lock(lock_); + DCHECK(0 < count_off_the_record_windows_); + if (0 >= count_off_the_record_windows_) // Defensive coding. + break; + if (--count_off_the_record_windows_) + break; // Still some windows are incognito. + } // Release lock. + OnTheRecord(true); + break; + + default: + break; + } + } + + private: + Lock lock_; + int count_off_the_record_windows_; + + DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordObserver); +}; + +// TODO(jar): Use static class object so that I don't have to get the +// destruction time right (which requires unregistering just before the +// notification-service shuts down). +static OffTheRecordObserver off_the_record_observer; + +//------------------------------------------------------------------------------ +// This section supports the about:dns page. +//------------------------------------------------------------------------------ + +// Provide global support for the about:dns page. +void DnsPrefetchGetHtmlInfo(std::string* output) { + output->append("<html><head><title>About DNS</title>" + // We'd like the following no-cache... but it doesn't work. + // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">" + "</head><body>"); + if (!dns_prefetch_enabled || NULL == dns_master) { + output->append("Dns Prefetching is disabled."); + } else { + if (!on_the_record_switch) { + output->append("Incognito mode is active in a window."); + } else { + dns_master->GetHtmlInfo(output); + PrefetchObserver::DnsGetFirstResolutionsHtml(output); + } + } + output->append("</body></html>"); +} + +//------------------------------------------------------------------------------ +// This section intializes and tears down global DNS prefetch services. +//------------------------------------------------------------------------------ + +// Note: We have explicit permission to create the following global static +// object (in opposition to Google style rules). By making it a static, we +// can ensure its deletion. +static PrefetchObserver dns_resolution_observer; + +void InitDnsPrefetch(PrefService* user_prefs) { + // Use a large shutdown time so that UI tests (that instigate lookups, and + // then try to shutdown the browser) don't instigate the CHECK about + // "some slaves have not finished" + const TimeDelta kAllowableShutdownTime(TimeDelta::FromSeconds(10)); + DCHECK(NULL == dns_master); + if (!dns_master) { + DnsMaster* new_master = new DnsMaster(kAllowableShutdownTime); + if (InterlockedCompareExchangePointer( + reinterpret_cast<PVOID*>(&dns_master), new_master, NULL)) { + delete new_master; + } else { + // We did the initialization, so we should prime the pump, and set up + // the DNS resolution system to run. + off_the_record_observer.Register(); + + if (user_prefs) { + bool enabled = user_prefs->GetBoolean(prefs::kDnsPrefetchingEnabled); + EnableDnsPrefetch(enabled); + } + + DLOG(INFO) << "DNS Prefetch service started"; + + // Start observing real HTTP stack resolutions. + net::AddDnsResolutionObserver(&dns_resolution_observer); + } + } +} + +void ShutdownDnsPrefetch() { + DCHECK(NULL != dns_master); + DnsMaster* master = dns_master; + dns_master = NULL; + if (master->ShutdownSlaves()) { + delete master; + } else { + // Leak instance if shutdown problem. + DCHECK(0); + } +} + +static void DiscardAllPrefetchState() { + if (!dns_master) + return; + dns_master->DiscardAllResults(); +} + +//------------------------------------------------------------------------------ +// Functions to handle saving of hostnames from one session to the next, to +// expedite startup times. + +void SaveHostNamesForNextStartup(PrefService* local_state) { + if (!dns_prefetch_enabled) + return; + PrefetchObserver::SaveStartupListAsPref(local_state); +} + +// TODO(jar): correct typo in name change ...Pretch... to ...Prefetch... +void DnsPretchHostNamesAtStartup(PrefService* user_prefs, + PrefService* local_state) { + NameList hostnames; + // Prefetch DNS for hostnames we learned about during last session. + // This may catch secondary hostnames, pulled in by the homepages. It will + // also catch more of the "primary" home pages, since that was (presumably) + // rendered first (and will be rendered first this time too). + ListValue* startup_list = + local_state->GetMutableList(prefs::kDnsStartupPrefetchList); + if (startup_list) { + for (ListValue::iterator it = startup_list->begin(); + it != startup_list->end(); + it++) { + std::wstring w_hostname; + (*it)->GetAsString(&w_hostname); + hostnames.push_back(WideToASCII(w_hostname)); + } + } + + // Prepare for any static home page(s) the user has in prefs. The user may + // have a LOT of tab's specified, so we may as well try to warm them all. + SessionStartupPref tab_start_pref = + SessionStartupPref::GetStartupPref(user_prefs); + if (SessionStartupPref::URLS == tab_start_pref.type) { + for (size_t i = 0; i < tab_start_pref.urls.size(); i++) { + GURL gurl = tab_start_pref.urls[i]; + if (gurl.is_valid() && !gurl.host().empty()) + hostnames.push_back(gurl.host()); + } + } + + if (hostnames.size() > 0) + DnsPrefetchList(hostnames); + else + DnsPrefetch(std::string("www.google.com")); // Start a thread. +} + + +} // namespace chrome_browser_net diff --git a/chrome/browser/net/dns_global.h b/chrome/browser/net/dns_global.h new file mode 100644 index 0000000..9fefffe --- /dev/null +++ b/chrome/browser/net/dns_global.h @@ -0,0 +1,80 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is the global interface for the dns prefetch services. It centralizes +// initialization, along with all the callbacks etc. to connect to the browser +// process. This allows the more standard DNS prefetching services, such as +// provided by DnsMaster to be left as more generally usable code, and possibly +// be shared across multiple client projects. + +#ifndef CHROME_BROWSER_NET_DNS_GLOBAL_H__ +#define CHROME_BROWSER_NET_DNS_GLOBAL_H__ + +#include "chrome/browser/net/dns_master.h" + +#include <string> + +class PrefService; + +namespace chrome_browser_net { + +// Functions to initialize our global state so that PrefetchDns() can be called. +void InitDnsPrefetch(PrefService* user_prefs); +void ShutdownDnsPrefetch(); + +// Global API relating to Prefetching in browser +void EnableDnsPrefetch(bool enable); +void RegisterPrefs(PrefService* local_state); +void RegisterUserPrefs(PrefService* user_prefs); +void DnsPrefetchList(const NameList& hostnames); +void DnsPrefetchUrlString(const url_canon::UTF16String& url_string); +void DnsPrefetch(const std::string& hostname); +void DnsPrefetchGetHtmlInfo(std::string* output); + +// Save the hostnames actually used at the start of this session to prefetch +// during the next startup. +void SaveHostNamesForNextStartup(PrefService* local_state); +void DnsPretchHostNamesAtStartup(PrefService* user_prefs, + PrefService* local_state); + +// Helper class to handle global init and shutdown. +class DnsPrefetcherInit { + public: + explicit DnsPrefetcherInit(PrefService* user_prefs) { + InitDnsPrefetch(user_prefs); + } + ~DnsPrefetcherInit() {ShutdownDnsPrefetch();} + + private: + DISALLOW_EVIL_CONSTRUCTORS(DnsPrefetcherInit); +}; + +} // namespace chrome_browser_net + +#endif // CHROME_BROWSER_NET_DNS_GLOBAL_H__ diff --git a/chrome/browser/net/dns_host_info.cc b/chrome/browser/net/dns_host_info.cc new file mode 100644 index 0000000..589e621 --- /dev/null +++ b/chrome/browser/net/dns_host_info.cc @@ -0,0 +1,361 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// See header file for description of class + +#include "chrome/browser/net/dns_host_info.h" + +#include <math.h> + +#include <algorithm> +#include <string> + +#include "base/histogram.h" +#include "base/logging.h" +#include "base/string_util.h" + +namespace chrome_browser_net { + +static bool detailed_logging_enabled = false; + +// Use command line switch to enable detailed logging. +void EnableDnsDetailedLog(bool enable) { + detailed_logging_enabled = enable; +} + +// static +int DnsHostInfo::sequence_counter = 1; + + +bool DnsHostInfo::NeedsDnsUpdate(const std::string& hostname) { + DCHECK(hostname == hostname_); + switch (state_) { + case PENDING: // Just now created info. + return true; + + case QUEUED: // In queue. + case ASSIGNED: // Slave is working on it. + case ASSIGNED_BUT_MARKED: // Slave is working on it. + return false; // We're already working on it + + case NO_SUCH_NAME: // Lookup failed. + case FOUND: // Lookup succeeded. + return !IsStillCached(); // See if DNS cache expired. + + default: + DCHECK(false); + return false; + } +} + +const TimeDelta DnsHostInfo::kNullDuration(TimeDelta::FromMilliseconds(-1)); + +TimeDelta DnsHostInfo::kCacheExpirationDuration(TimeDelta::FromMinutes(5)); + +const TimeDelta DnsHostInfo::kMaxNonNetworkDnsLookupDuration( + TimeDelta::FromMilliseconds(15)); + +void DnsHostInfo::set_cache_expiration(TimeDelta time) { + kCacheExpirationDuration = time; +} + +void DnsHostInfo::SetQueuedState() { + DCHECK(PENDING == state_ || FOUND == state_ || NO_SUCH_NAME == state_); + state_ = QUEUED; + queue_duration_ = resolve_duration_ = kNullDuration; + GetDuration(); // Set time_ + DLogResultsStats("DNS Prefetch in queue"); +} + +void DnsHostInfo::SetAssignedState() { + DCHECK(QUEUED == state_); + state_ = ASSIGNED; + queue_duration_ = GetDuration(); + DLogResultsStats("DNS Prefetch assigned"); + DHISTOGRAM_TIMES(L"DNS.PrefetchQueue", queue_duration_); +} + +void DnsHostInfo::SetPendingDeleteState() { + DCHECK(ASSIGNED == state_ || ASSIGNED_BUT_MARKED == state_); + state_ = ASSIGNED_BUT_MARKED; +} + +void DnsHostInfo::SetFoundState() { + DCHECK(ASSIGNED == state_); + state_ = FOUND; + resolve_duration_ = GetDuration(); + if (kMaxNonNetworkDnsLookupDuration <= resolve_duration_) { + UMA_HISTOGRAM_LONG_TIMES(L"DNS.PrefetchFoundNameL", resolve_duration_); + // Record potential beneficial time, and maybe we'll get a cache hit. + // We keep the maximum, as the warming we did earlier may still be + // helping with a cache upstream in DNS resolution. + benefits_remaining_ = std::max(resolve_duration_, benefits_remaining_); + } + sequence_number_ = sequence_counter++; + DLogResultsStats("DNS PrefetchFound"); +} + +void DnsHostInfo::SetNoSuchNameState() { + DCHECK(ASSIGNED == state_); + state_ = NO_SUCH_NAME; + resolve_duration_ = GetDuration(); + if (kMaxNonNetworkDnsLookupDuration <= resolve_duration_) { + DHISTOGRAM_TIMES(L"DNS.PrefetchNotFoundName", resolve_duration_); + // Record potential beneficial time, and maybe we'll get a cache hit. + benefits_remaining_ = std::max(resolve_duration_, benefits_remaining_); + } + sequence_number_ = sequence_counter++; + DLogResultsStats("DNS PrefetchNotFound"); +} + +void DnsHostInfo::SetStartedState() { + DCHECK(PENDING == state_); + state_ = STARTED; + queue_duration_ = resolve_duration_ = TimeDelta(); // 0ms. + GetDuration(); // Set time. +} + +void DnsHostInfo::SetFinishedState(bool was_resolved) { + DCHECK(STARTED == state_); + state_ = was_resolved ? FINISHED : FINISHED_UNRESOLVED; + resolve_duration_ = GetDuration(); + // TODO(jar): Sequence number should be incremented in prefetched HostInfo. + DLogResultsStats("DNS HTTP Finished"); +} + +// IsStillCached() guesses if the DNS cache still has IP data, +// or at least remembers results about "not finding host." +bool DnsHostInfo::IsStillCached() const { + DCHECK(FOUND == state_ || NO_SUCH_NAME == state_); + + // Default MS OS does not cache failures. Hence we could return false almost + // all the time for that case. However, we'd never try again to prefetch + // the value if we returned false that way. Hence we'll just let the lookup + // time out the same way as FOUND case. + + if (sequence_counter - sequence_number_ > kMaxGuaranteedCacheSize) + return false; + + TimeDelta time_since_resolution = TimeTicks::Now() - time_; + + if (FOUND == state_ && resolve_duration_ < kMaxNonNetworkDnsLookupDuration) { + // Since cache was warm (no apparent network activity during resolution), + // we assume it was "really" found (via network activity) twice as long + // ago as when we got our FOUND result. + time_since_resolution *= 2; + } + + return time_since_resolution < kCacheExpirationDuration; +} + +// Compare the later results, to the previously prefetched info. +DnsBenefit DnsHostInfo::AcruePrefetchBenefits(DnsHostInfo* later_host_info) { + DCHECK(FINISHED == later_host_info->state_ + || FINISHED_UNRESOLVED == later_host_info->state_); + DCHECK(0 == later_host_info->hostname_.compare(hostname_.data())); + if ((0 == benefits_remaining_.InMilliseconds()) || + (FOUND != state_ && NO_SUCH_NAME != state_)) { + return PREFETCH_NO_BENEFIT; + } + + TimeDelta benefit = benefits_remaining_ - later_host_info->resolve_duration_; + later_host_info->benefits_remaining_ = benefits_remaining_; + benefits_remaining_ = TimeDelta(); // zero ms. + + if (later_host_info->resolve_duration_ > kMaxNonNetworkDnsLookupDuration) { + // Our precache effort didn't help since HTTP stack hit the network. + DHISTOGRAM_TIMES(L"DNS.PrefetchCacheEviction", resolve_duration_); + DLogResultsStats("DNS PrefetchCacheEviction"); + return PREFETCH_CACHE_EVICTION; + } + + if (NO_SUCH_NAME == state_) { + UMA_HISTOGRAM_LONG_TIMES(L"DNS.PrefetchNegativeHitL", benefit); + DLogResultsStats("DNS PrefetchNegativeHit"); + return PREFETCH_NAME_NONEXISTANT; + } + + DCHECK_EQ(FOUND, state_); + UMA_HISTOGRAM_LONG_TIMES(L"DNS.PrefetchPositiveHitL", benefit); + DLogResultsStats("DNS PrefetchPositiveHit"); + return PREFETCH_NAME_FOUND; +} + +void DnsHostInfo::DLogResultsStats(const char* message) const { + if (!detailed_logging_enabled) + return; + DLOG(INFO) << "\t" << message << "\tq=" + << queue_duration().InMilliseconds() << "ms,\tr=" + << resolve_duration().InMilliseconds() << "ms\tp=" + << benefits_remaining_.InMilliseconds() << "ms\tseq=" + << sequence_number_ + << "\t" << hostname_; +} + +//------------------------------------------------------------------------------ +// This last section supports HTML output, such as seen in about:dns. +//------------------------------------------------------------------------------ + +// Preclude any possibility of Java Script or markup in the text, by only +// allowing alphanumerics, ".", and whitespace. +static std::string RemoveJs(const std::string& text) { + std::string output(text); + size_t length = output.length(); + for (size_t i = 0; i < 1; i++) { + char next = output[i]; + if (isalnum(next) || isspace(next) || '.' == next) + continue; + output[i] = '?'; + } + return output; +} + +class MinMaxAverage { + public: + MinMaxAverage() + : sum_(0), square_sum_(0), count_(0), + minimum_(kint64max), maximum_(kint64min) { + } + + // Return values for use in printf formatted as "%d" + int sample(int64 value) { + sum_ += value; + square_sum_ += value * value; + count_++; + minimum_ = std::min(minimum_, value); + maximum_ = std::max(maximum_, value); + return static_cast<int>(value); + } + int minimum() const { return static_cast<int>(minimum_); } + int maximum() const { return static_cast<int>(maximum_); } + int average() const { return static_cast<int>(sum_/count_); } + int sum() const { return static_cast<int>(sum_); } + + int standard_deviation() const { + double average = static_cast<float>(sum_) / count_; + double variance = static_cast<float>(square_sum_)/count_ + - average * average; + return static_cast<int>(floor(sqrt(variance) + .5)); + } + + private: + int64 sum_; + int64 square_sum_; + int count_; + int64 minimum_; + int64 maximum_; + + // DISALLOW_EVIL_CONSTRUCTORS(MinMaxAverage); +}; + +static std::string HoursMinutesSeconds(int seconds) { + std::string result; + int print_seconds = seconds % 60; + int minutes = seconds / 60; + int print_minutes = minutes % 60; + int print_hours = minutes/60; + if (print_hours) + StringAppendF(&result, "%.2d:", print_hours); + if (print_hours || print_minutes) + StringAppendF(&result, "%2.2d:", print_minutes); + StringAppendF(&result, "%2.2d", print_seconds); + return result; +} + +// static +void DnsHostInfo::GetHtmlTable(const DnsInfoTable host_infos, + const char* description, + const bool brief, + std::string* output) { + if (0 == host_infos.size()) + return; + output->append(description); + StringAppendF(output, "%d %s", host_infos.size(), + (1 == host_infos.size()) ? "hostname" : "hostnames"); + + if (brief) { + output->append("<br><br>"); + return; + } + + char* row_format = "<tr align=right><td>%s</td>" + "<td>%d</td><td>%d</td><td>%s</td></tr>"; + + output->append("<br><table border=1>"); + StringAppendF(output, "<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>", + "Host name", "Applicable Prefetch<br>Time (ms)", + "Recent Resolution<br>Time(ms)", "How long ago<br>(HH:MM:SS)"); + + // Print bulk of table, and gather stats at same time. + MinMaxAverage queue, resolve, preresolve, when; + TimeTicks current_time = TimeTicks::Now(); + for (DnsInfoTable::const_iterator it(host_infos.begin()); + it != host_infos.end(); it++) { + queue.sample((it->queue_duration_.InMilliseconds())); + StringAppendF(output, row_format, + RemoveJs(it->hostname_).c_str(), + preresolve.sample((it->benefits_remaining_.InMilliseconds())), + resolve.sample((it->resolve_duration_.InMilliseconds())), + HoursMinutesSeconds(when.sample( + (current_time - it->time_).InSeconds())).c_str()); + } + // Write min, max, and average summary lines. + if (host_infos.size() > 2) { + output->append("<B>"); + StringAppendF(output, row_format, + "<b>---minimum---</b>", + preresolve.minimum(), resolve.minimum(), + HoursMinutesSeconds(when.minimum()).c_str()); + StringAppendF(output, row_format, + "<b>---average---</b>", + preresolve.average(), resolve.average(), + HoursMinutesSeconds(when.average()).c_str()); + StringAppendF(output, row_format, + "<b>standard deviation</b>", + preresolve.standard_deviation(), + resolve.standard_deviation(), "n/a"); + StringAppendF(output, row_format, + "<b>---maximum---</b>", + preresolve.maximum(), resolve.maximum(), + HoursMinutesSeconds(when.maximum()).c_str()); + StringAppendF(output, row_format, + "<b>-----SUM-----</b>", + preresolve.sum(), resolve.sum(), "n/a"); + } + output->append("</table>"); + +#ifdef DEBUG + StringAppendF(output, + "Prefetch Queue Durations: min=%d, avg=%d, max=%d<br><br>", + queue.minimum(), queue.average(), queue.maximum()); +#endif + + output->append("<br>"); +} +} // namespace chrome_browser_net diff --git a/chrome/browser/net/dns_host_info.h b/chrome/browser/net/dns_host_info.h new file mode 100644 index 0000000..d0a05bc --- /dev/null +++ b/chrome/browser/net/dns_host_info.h @@ -0,0 +1,174 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A DnsHostInfo object is used to store status of a Dns lookup of a specific +// hostname. +// It includes progress, from placement in the DnsMaster's queue, to assignment +// to a slave, to resolution by the (blocking) DNS service as either FOUND or +// NO_SUCH_NAME. + +#ifndef CHROME_BROWSER_NET_DNS_HOST_INFO_H__ +#define CHROME_BROWSER_NET_DNS_HOST_INFO_H__ + +#include <string> +#include <vector> + +#include "base/logging.h" +#include "base/time.h" + +namespace chrome_browser_net { + +// Use command line switch to enable detailed logging. +void EnableDnsDetailedLog(bool enable); + +enum DnsBenefit { + PREFETCH_NO_BENEFIT, // Prefetch never hit the network. Name was pre-cached. + PREFETCH_CACHE_EVICTION, // Prefetch used network, but so did HTTP stack. + PREFETCH_NAME_NONEXISTANT, // Valuable prefetch of "name not found" was used. + PREFETCH_NAME_FOUND, // Valuable prefetch was used. + PREFETCH_OBLIVIOUS // No prefetch attempt was even made. +}; + +class DnsHostInfo { + public: + enum DnsProcessingState { + // When processed by our prefetching system, there are 4 states: + PENDING, // Constructor has completed. + QUEUED, // In prefetch queue but not yet assigned to a slave. + ASSIGNED, // Currently being processed by a slave. + ASSIGNED_BUT_MARKED, // Needs to be deleted as soon as slave is done. + FOUND, // DNS prefetch search completed. + NO_SUCH_NAME, // DNS prefetch search completed. + // When processed by the HTTP network stack, there are 3 states: + STARTED, // Resolution has begun. + FINISHED, // Resolution has completed. + FINISHED_UNRESOLVED}; // No resolution found. + static const TimeDelta kMaxNonNetworkDnsLookupDuration; + // The number of OS cache entries we can guarantee(?) before cache eviction + // might likely take place. + static const int kMaxGuaranteedCacheSize = 50; + + typedef std::vector<const DnsHostInfo> DnsInfoTable; + + static const TimeDelta kNullDuration; + + // DnsHostInfo are usually made by the default constructor during + // initializing of the DnsMaster's map (of info for Hostnames). + DnsHostInfo() + : state_(PENDING), + resolve_duration_(kNullDuration), + queue_duration_(kNullDuration), + benefits_remaining_(), + sequence_number_(0) { + } + + ~DnsHostInfo() {} + + // NeedDnsUpdate decides, based on our internal info, + // if it would be valuable to attempt to update (prefectch) + // DNS data for hostname. This decision is based + // on how recently we've done DNS prefetching for hostname. + bool NeedsDnsUpdate(const std::string& hostname); + + static void set_cache_expiration(TimeDelta time); + + // The prefetching lifecycle. + void SetQueuedState(); + void SetAssignedState(); + void SetPendingDeleteState(); + void SetFoundState(); + void SetNoSuchNameState(); + // The actual browsing resolution lifecycle. + void SetStartedState(); + void SetFinishedState(bool was_resolved); + + void SetHostname(const std::string& hostname) { + if (hostname != hostname_) { + DCHECK(hostname_.size() == 0); // Not yet initialized. + hostname_ = hostname; + } + } + + bool was_found() const { return FOUND == state_; } + bool was_nonexistant() const { return NO_SUCH_NAME == state_; } + bool is_assigned() const { + return ASSIGNED == state_ || ASSIGNED_BUT_MARKED == state_; + } + bool is_marked_to_delete() const { return ASSIGNED_BUT_MARKED == state_; } + const std::string hostname() const { return hostname_; } + + bool HasHostname(const std::string& hostname) const { + return (hostname == hostname_); + } + + TimeDelta resolve_duration() const { return resolve_duration_;} + TimeDelta queue_duration() const { return queue_duration_;} + TimeDelta benefits_remaining() const { return benefits_remaining_; } + + DnsBenefit AcruePrefetchBenefits(DnsHostInfo* later_host_info); + + void DLogResultsStats(const char* message) const; + + static void GetHtmlTable(const DnsInfoTable host_infos, + const char* description, + const bool brief, + std::string* output); + + private: + // The next declaration is non-const to facilitate testing. + static TimeDelta kCacheExpirationDuration; + + DnsProcessingState state_; + std::string hostname_; // Hostname for this info. + + TimeTicks time_; // When was last state changed (usually lookup completed). + TimeDelta resolve_duration_; // Time needed for DNS to resolve. + TimeDelta queue_duration_; // Time spent in queue. + TimeDelta benefits_remaining_; // Unused potential benefits of a prefetch. + + int sequence_number_; // Used to calculate potential of cache eviction. + static int sequence_counter; // Used to allocate sequence_number_'s. + + TimeDelta GetDuration() { + TimeTicks old_time = time_; + time_ = TimeTicks::Now(); + return time_ - old_time; + } + + // IsStillCached() guesses if the DNS cache still has IP data. + bool IsStillCached() const; + + // We put these objects into a std::map, and hence we + // need some "evil" constructors. + // DISALLOW_EVIL_CONSTRUCTORS(DnsHostInfo); +}; + +} // namespace chrome_browser_net + +#endif // CHROME_BROWSER_NET_DNS_HOST_INFO_H__ diff --git a/chrome/browser/net/dns_host_info_unittest.cc b/chrome/browser/net/dns_host_info_unittest.cc new file mode 100644 index 0000000..1767fc4 --- /dev/null +++ b/chrome/browser/net/dns_host_info_unittest.cc @@ -0,0 +1,114 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Single threaded tests of DnsHostInfo functionality. + +#include <time.h> +#include <string> + +#include "chrome/browser/net/dns_host_info.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class DnsHostInfoTest : public testing::Test { +}; + +typedef chrome_browser_net::DnsHostInfo DnsHostInfo; + +TEST(DnsHostInfoTest, StateChangeTest) { + DnsHostInfo info_practice, info; + std::string hostname1("domain1.com"), hostname2("domain2.com"); + + // First load DLL, so that their load time won't interfere with tests. + // Some tests involve timing function performance, and DLL time can overwhelm + // test durations (which are considering network vs cache response times). + info_practice.SetHostname(hostname2); + info_practice.SetQueuedState(); + info_practice.SetAssignedState(); + info_practice.SetFoundState(); + Sleep(500); // Allow time for DLLs to fully load. + + // Complete the construction of real test object. + info.SetHostname(hostname1); + + EXPECT_TRUE(info.NeedsDnsUpdate(hostname1)) << "error in construction state"; + info.SetQueuedState(); + EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) + << "update needed after being queued"; + info.SetAssignedState(); + EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) + << "update needed while assigned to slave"; + info.SetFoundState(); + EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) + << "default expiration time is TOOOOO short"; + + // Note that time from ASSIGNED to FOUND was VERY short (probably 0ms), so the + // object should conclude that no network activity was needed. As a result, + // the required time till expiration will be halved (guessing that we were + // half way through having the cache expire when we did the lookup. + EXPECT_LT(info.resolve_duration().InMilliseconds(), + DnsHostInfo::kMaxNonNetworkDnsLookupDuration.InMilliseconds()) + << "Non-net time is set too low"; + + info.set_cache_expiration(TimeDelta::FromMilliseconds(300)); + EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored"; + // Note that we'll actually get an expiration (effectively) of + // 150ms, since there was no detected network activity time during lookup. + Sleep(80); // Not enough time to pass our 150ms mark. + EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored"; + Sleep(100); // Be sure we sleep (80+100) enough to pass that 150ms mark. + EXPECT_TRUE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored"; + + // That was a nice life when the object was found.... but next time it won't + // be found. We'll sleep for a while, and then come back with not-found. + info.SetQueuedState(); + info.SetAssignedState(); + EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) + << "update needed while assigned to slave"; + Sleep(25); // Greater than minimal expected network latency on DNS lookup. + info.SetNoSuchNameState(); + EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) + << "default expiration time is TOOOOO short"; + + // Note that now we'll actually utilize an expiration of 300ms, + // since there was detected network activity time during lookup. + // We're assuming the caching just started with our lookup. + Sleep(80); // Not enough time to pass our 300ms mark. + EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored"; + Sleep(80); // Still not past our 300ms mark (only about 4+2ms) + EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored"; + Sleep(150); + EXPECT_TRUE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored"; +} + +// TODO(jar): Add death test for illegal state changes, and also for setting +// hostname when already set. + +} // namespace anonymous diff --git a/chrome/browser/net/dns_master.cc b/chrome/browser/net/dns_master.cc new file mode 100644 index 0000000..493c404 --- /dev/null +++ b/chrome/browser/net/dns_master.cc @@ -0,0 +1,454 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// See header file for description of class + +#include "chrome/browser/net/dns_master.h" + +#include <map> +#include <sstream> +#include <string> + +#include "base/condition_variable.h" +#include "base/histogram.h" +#include "base/stats_counters.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "base/win_util.h" +#include "chrome/browser/net/dns_slave.h" + +namespace chrome_browser_net { + +//------------------------------------------------------------------------------ +// This section contains methods for the DnsMaster class. +//------------------------------------------------------------------------------ +DnsMaster::DnsMaster(TimeDelta shutdown_wait_time) + : slave_count_(0), + shutdown_(false), + running_slave_count_(0), + kShutdownWaitTime_(shutdown_wait_time), + slaves_have_work_(&lock_) { + for ( int i = 0; i < kSlaveCountMax; i++ ) { + thread_ids_[i] = 0; + thread_handles_[i] = 0; + slaves_[i] = NULL; + } +} + +// Overloaded Resolve() to take a vector of names. +void DnsMaster::ResolveList(const NameList& hostnames) { + { + AutoLock auto_lock(lock_); + if (shutdown_) return; + if (slave_count_ < kSlaveCountMin) { + for (int target_count = std::min(static_cast<int>(hostnames.size()), + kSlaveCountMin); + target_count > 0; + target_count--) + PreLockedCreateNewSlaveIfNeeded(); + } else { + PreLockedCreateNewSlaveIfNeeded(); // Allocate one per list call. + } + + for (NameList::const_iterator it = hostnames.begin(); + it < hostnames.end(); + it++) { + PreLockedResolve(*it); + } + } + slaves_have_work_.Signal(); +} + +// Basic Resolve() takes an invidual name, and adds it +// to the queue. +void DnsMaster::Resolve(const std::string& hostname) { + if (0 == hostname.length()) + return; + { + AutoLock auto_lock(lock_); + if (shutdown_) return; + PreLockedCreateNewSlaveIfNeeded(); // Allocate one at a time. + PreLockedResolve(hostname); + } + slaves_have_work_.Signal(); +} + +bool DnsMaster::AcruePrefetchBenefits(DnsHostInfo* host_info) { + std::string hostname = host_info->hostname(); + DnsBenefit benefit; + DnsHostInfo prefetched_host_info; + { + AutoLock auto_lock(lock_); + if (results_.find(hostname) == results_.end()) { + // Remain under lock to assure static HISTOGRAM constructor is safely run. + // Use UMA histogram to quantify potential future gains here. + UMA_HISTOGRAM_LONG_TIMES(L"DNS.UnexpectedResolutionL", + host_info->resolve_duration()); + SIMPLE_STATS_COUNTER(L"DNS.PrefetchCacheOblivious"); + host_info->DLogResultsStats("DNS PrefetchCacheOblivious"); + return false; + } + benefit = results_[hostname].AcruePrefetchBenefits(host_info); + switch (benefit) { + case PREFETCH_NAME_FOUND: + case PREFETCH_NAME_NONEXISTANT: + // Remain under lock to push data. + cache_hits_.push_back(*host_info); + return true; + + case PREFETCH_CACHE_EVICTION: + // Remain under lock to push data. + cache_eviction_map_[hostname] = *host_info; + return false; + case PREFETCH_NO_BENEFIT: + // Prefetch never hit the network. Name was pre-cached. + return false; + + default: + DCHECK(false); + return false; + } + } +} + +static char* PluralOptionalHostname(size_t count) { + if (count == 1) + return "hostname"; + return "hostnames"; +} + +// Provide sort order so all .com's are together, etc. +struct RightToLeftStringSorter { + bool operator()(const std::string& left, const std::string& right) const { + size_t left_length = left.length(); + size_t right_length = right.length(); + const char* left_data = left.data(); + const char* right_data = right.data(); + while (true) { + if (0 == right_length) + return false; + if (0 == left_length) + return true; + left_length--; + right_length--; + int difference = left_data[left_length] - right_data[right_length]; + if (difference) + return difference < 0; + } + } +}; + +void DnsMaster::GetHtmlInfo(std::string* output) { + // Local lists for calling DnsHostInfo + DnsHostInfo::DnsInfoTable cache_hits; + DnsHostInfo::DnsInfoTable cache_evictions; + DnsHostInfo::DnsInfoTable name_not_found; + DnsHostInfo::DnsInfoTable network_hits; + DnsHostInfo::DnsInfoTable already_cached; + + // Get copies of all useful data under protection of a lock. + typedef std::map<std::string, DnsHostInfo, RightToLeftStringSorter> Snapshot; + Snapshot snapshot; + { + AutoLock auto_lock(lock_); + // DnsHostInfo supports value semantics, so we can do a shallow copy. + for (Results::iterator it(results_.begin()); it != results_.end(); it++) { + snapshot[it->first] = it->second; + } + for (Results::iterator it(cache_eviction_map_.begin()); + it != cache_eviction_map_.end(); + it++) { + cache_evictions.push_back(it->second); + } + // Reverse list as we copy cache hits, so that new hits are at the top. + size_t index = cache_hits_.size(); + while (index > 0) { + index--; + cache_hits.push_back(cache_hits_[index]); + } + } + + // Partition the DnsHostInfo's into categories. + for (Snapshot::iterator it(snapshot.begin()); it != snapshot.end(); it++) { + if (it->second.was_nonexistant()) { + name_not_found.push_back(it->second); + continue; + } + if (!it->second.was_found()) + continue; // Still being processed. + if (TimeDelta() != it->second.benefits_remaining()) { + network_hits.push_back(it->second); // With no benefit yet. + continue; + } + if (DnsHostInfo::kMaxNonNetworkDnsLookupDuration > + it->second.resolve_duration()) { + already_cached.push_back(it->second); + continue; + } + // Remaining case is where prefetch benefit was significant, and was used. + // Since we shot those cases as historical hits, we won't bother here. + } + + bool brief = false; +#ifdef NDEBUG + brief = true; +#endif // NDEBUG + + // Call for display of each table, along with title. + DnsHostInfo::GetHtmlTable(cache_hits, + "Prefetching DNS records produced benefits for ", false, output); + DnsHostInfo::GetHtmlTable(cache_evictions, + "Cache evictions negated DNS prefetching benefits for ", brief, output); + DnsHostInfo::GetHtmlTable(network_hits, + "Prefetching DNS records was not yet beneficial for ", brief, output); + DnsHostInfo::GetHtmlTable(already_cached, + "Previously cached resolutions were found for ", brief, output); + DnsHostInfo::GetHtmlTable(name_not_found, + "Prefetching DNS records revealed non-existance for ", brief, output); +} + +void DnsMaster::PreLockedResolve(const std::string& hostname) { + // DCHECK(We have the lock); + DCHECK(0 != slave_count_); + DCHECK(0 != hostname.length()); + + DnsHostInfo* info = &results_[hostname]; + info->SetHostname(hostname); // Initialize or DCHECK. + // TODO(jar): I need to discard names that have long since expired. + // Currently we only add to the domain map :-/ + + DCHECK(info->HasHostname(hostname)); + + static StatsCounter count(L"DNS.PrefetchContemplated"); + count.Increment(); + + if (!info->NeedsDnsUpdate(hostname)) { + info->DLogResultsStats("DNS PrefetchNotUpdated"); + return; + } + + info->SetQueuedState(); + name_buffer_.push(hostname); +} + +void DnsMaster::SetSlaveName(int slave_index) { + DCHECK(0 <= slave_index && kSlaveCountMax > slave_index); + + std::string name = StringPrintf("Dns Prefetcher thread %d of %d", + slave_index + 1, kSlaveCountMax); + + DLOG(INFO) << "Now Running " << name; + Thread::SetThreadName(name.c_str(), thread_ids_[slave_index]); +} + +// GetNextAssignment() is executed on the thread associated with +// with a prefetch slave instance. +// Return value of false indicates slave thread termination is needed. +// Return value of true means info argument was populated +// with a pointer to the assigned DnsHostInfo instance. +bool DnsMaster::GetNextAssignment(std::string* hostname) { + bool ask_for_help = false; + { + AutoLock auto_lock(lock_); // For map and buffer access + while (0 == name_buffer_.size() && !shutdown_) { + // No work pending, so just wait. + // wait on condition variable while releasing lock temporarilly. + slaves_have_work_.Wait(); + } + if (shutdown_) + return false; // Tell slaves to terminate also. + *hostname = name_buffer_.front(); + name_buffer_.pop(); + + DnsHostInfo* info = &results_[*hostname]; + DCHECK(info->HasHostname(*hostname)); + info->SetAssignedState(); + + ask_for_help = name_buffer_.size() > 0; + } // Release lock_ + if (ask_for_help) + slaves_have_work_.Signal(); + return true; +} + +void DnsMaster::SetFoundState(const std::string hostname) { + AutoLock auto_lock(lock_); // For map access (changing info values). + DnsHostInfo* info = &results_[hostname]; + DCHECK(info->HasHostname(hostname)); + if (info->is_marked_to_delete()) + results_.erase(hostname); + else + info->SetFoundState(); +} + +void DnsMaster::SetNoSuchNameState(const std::string hostname) { + AutoLock auto_lock(lock_); // For map access (changing info values). + DnsHostInfo* info = &results_[hostname]; + DCHECK(info->HasHostname(hostname)); + if (info->is_marked_to_delete()) + results_.erase(hostname); + else + info->SetNoSuchNameState(); +} + +bool DnsMaster::PreLockedCreateNewSlaveIfNeeded() { + // Don't create more then max. + if (kSlaveCountMax <= slave_count_ || shutdown_) + return false; + + DnsSlave* slave_instance = new DnsSlave(this, slave_count_); + DWORD thread_id; + size_t stack_size = 0; + unsigned int flags = CREATE_SUSPENDED; + if (win_util::GetWinVersion() >= win_util::WINVERSION_XP) { + // 128kb stack size. + stack_size = 128*1024; + flags |= STACK_SIZE_PARAM_IS_A_RESERVATION; + } + HANDLE handle = CreateThread(NULL, // security + stack_size, + DnsSlave::ThreadStart, + reinterpret_cast<void*>(slave_instance), + flags, + &thread_id); + DCHECK(NULL != handle); + if (NULL == handle) + return false; + + // Carlos suggests it is not valuable to do a set priority. + // BOOL WINAPI SetThreadPriority(handle,int nPriority); + + thread_ids_[slave_count_] = thread_id; + thread_handles_[slave_count_] = handle; + slaves_[slave_count_] = slave_instance; + slave_count_++; + + ResumeThread(handle); // WINAPI call. + running_slave_count_++; + + return true; +} + +void DnsMaster::SetSlaveHasTerminated(int slave_index) { + DCHECK_EQ(GetCurrentThreadId(), thread_ids_[slave_index]); + AutoLock auto_lock(lock_); + running_slave_count_--; + DCHECK(thread_ids_[slave_index]); + thread_ids_[slave_index] = 0; +} + +bool DnsMaster::ShutdownSlaves() { + int running_slave_count; + { + AutoLock auto_lock(lock_); + shutdown_ = true; // Block additional resolution requests. + // Empty the queue gracefully + while (name_buffer_.size() > 0) { + std::string hostname = name_buffer_.front(); + name_buffer_.pop(); + DnsHostInfo* info = &results_[hostname]; + DCHECK(info->HasHostname(hostname)); + // We should be in Queued state, so simulate to end of life. + info->SetAssignedState(); // Simulate slave assignment. + info->SetNoSuchNameState(); // Simulate failed lookup. + results_.erase(hostname); + } + running_slave_count = running_slave_count_; + // Release lock, so slaves can finish up. + } + + if (running_slave_count) { + slaves_have_work_.Broadcast(); // Slaves need to check for termination. + + DWORD result = WaitForMultipleObjects( + slave_count_, + thread_handles_, + TRUE, // Wait for all + static_cast<DWORD>(kShutdownWaitTime_.InMilliseconds())); + + DCHECK(result != WAIT_TIMEOUT) << "Some slaves didn't stop"; + if (WAIT_TIMEOUT == result) + return false; + } + { + AutoLock auto_lock(lock_); + while (0 < slave_count_--) { + if (0 == thread_ids_[slave_count_]) { // Thread terminated. + int result = CloseHandle(thread_handles_[slave_count_]); + CHECK(0 != result); + thread_handles_[slave_count_] = 0; + delete slaves_[slave_count_]; + slaves_[slave_count_] = NULL; + } + } + } + return true; +} + +void DnsMaster::DiscardAllResults() { + AutoLock auto_lock(lock_); + // Delete anything listed so far in this session that shows in about:dns. + cache_eviction_map_.clear(); + cache_hits_.clear(); + + + // Try to delete anything in our work queue. + while (!name_buffer_.empty()) { + // Emulate processing cycle as though host was not found. + std::string hostname = name_buffer_.front(); + name_buffer_.pop(); + DnsHostInfo* info = &results_[hostname]; + DCHECK(info->HasHostname(hostname)); + info->SetAssignedState(); + info->SetNoSuchNameState(); + } + // Now every result_ is either resolved, or is being worked on by a slave. + + // Step through result_, recording names of all hosts that can't be erased. + // We can't erase anything being worked on by a slave. + Results assignees; + for (Results::iterator it = results_.begin(); results_.end() != it; ++it) { + std::string hostname = it->first; + DnsHostInfo* info = &it->second; + DCHECK(info->HasHostname(hostname)); + if (info->is_assigned()) { + info->SetPendingDeleteState(); + assignees[hostname] = *info; + } + } + DCHECK(kSlaveCountMax >= assignees.size()); + results_.clear(); + // Put back in the names being worked on by slaves. + for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) { + DCHECK(it->second.is_marked_to_delete()); + results_[it->first] = it->second; + } +} + +} // namespace chrome_browser_net diff --git a/chrome/browser/net/dns_master.h b/chrome/browser/net/dns_master.h new file mode 100644 index 0000000..45dd00e --- /dev/null +++ b/chrome/browser/net/dns_master.h @@ -0,0 +1,201 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A DnsMaster object is instantiated once in the browser +// process, and delivers DNS prefetch assignments (hostnames) +// to any of several DnsSlave objects. +// Most hostname lists are sent out by renderer processes, and +// involve lists of hostnames that *might* be used in the near +// future by the browsing user. The goal of this class is to +// cause the underlying DNS structure to lookup a hostname before +// it is really needed, and hence reduce latency in the standard +// lookup paths. Since some DNS lookups may take a LONG time, we +// use several DnsSlave threads to concurrently perform the +// lookups. + +#ifndef CHROME_BROWSER_NET_DNS_MASTER_H__ +#define CHROME_BROWSER_NET_DNS_MASTER_H__ + +#include <map> +#include <queue> +#include <string> + +#include "base/condition_variable.h" +#include "base/scoped_ptr.h" +#include "chrome/browser/net/dns_host_info.h" +#include "chrome/common/net/dns.h" +#include "googleurl/src/url_canon.h" + +namespace chrome_browser_net { + +class DnsSlave; + +typedef chrome_common_net::NameList NameList; +typedef std::map<std::string, DnsHostInfo> Results; + +class DnsMaster { + public: + + explicit DnsMaster(TimeDelta shutdown_wait_time); + + ~DnsMaster() { + if (!shutdown_) + ShutdownSlaves(); // Ensure we did our cleanup. + } + + // ShutdownSlaves() gets all spawned threads to terminate, closes + // their handles, and deletes their DnsSlave instances. + // Return value of true means all operations succeeded. + // Return value of false means that the threads wouldn't terminate, + // and that resources may leak. If this returns false, it is best + // to NOT delete this DnsMaster, as slave threads may still call into + // this object. + bool ShutdownSlaves(); + + // In some circumstances, for privacy reasons, all results should be + // discarded. This method gracefully handles that activity. + // Destroy all our internal state, which shows what names we've looked up, and + // how long each has taken, etc. etc. We also destroy records of suggesses + // (cache hits etc.). + void DiscardAllResults(); + + // Add hostname(s) to the queue for processing by slaves + void ResolveList(const NameList& hostnames); + void Resolve(const std::string& hostname); + + // Get latency benefit of the prefetch that we are navigating to. + bool AcruePrefetchBenefits(DnsHostInfo* host_info); + + void GetHtmlInfo(std::string* output); + + // For testing only... + // Currently testing only provides a crude measure of success. + bool WasFound(const std::string& hostname) { + AutoLock auto_lock(lock_); + return (results_.find(hostname) != results_.end()) && + results_[hostname].was_found(); + } + + // Accessor methods, used mostly for testing. + // Both functions return DnsHostInfo::kNullDuration if name was not yet + // processed enough. + TimeDelta GetResolutionDuration(const std::string hostname) { + AutoLock auto_lock(lock_); + if (results_.find(hostname) == results_.end()) + return DnsHostInfo::kNullDuration; + return results_[hostname].resolve_duration(); + } + + TimeDelta GetQueueDuration(const std::string hostname) { + AutoLock auto_lock(lock_); + if (results_.find(hostname) == results_.end()) + return DnsHostInfo::kNullDuration; + return results_[hostname].queue_duration(); + } + + int running_slave_count() { + AutoLock auto_lock(lock_); + return running_slave_count_; + } + + //---------------------------------------------------------------------------- + // Methods below this line should only be called by slave processes. + + // Thread names can only be set after the thread has been running a bit. + void SetSlaveName(int slave_index); + + // GetNextAssignment() gets the next hostname from queue for processing + // It is not meant to be public, and should only be used by the slave. + // GetNextAssignment() waits on a condition variable if there are no more + // names in queue. + // Return false if slave thread should terminate. + // Return true if slave thread should process the value. + bool GetNextAssignment(std::string* hostname); + + // Access methods for use by slave threads to callback with state updates. + void SetFoundState(const std::string hostname); + void SetNoSuchNameState(const std::string hostname); + + // Notification during ShutdownSlaves. + void SetSlaveHasTerminated(int slave_index); + + private: + //---------------------------------------------------------------------------- + // Internal helper functions + + // "PreLocked" means that the caller has already Acquired lock_ in the + // following method names. + void PreLockedResolve(const std::string& hostname); + bool PreLockedCreateNewSlaveIfNeeded(); // Lazy slave processes creation. + + // The number of slave processes that will do DNS prefetching + static const int kSlaveCountMax = 8; + // Number of slave processes started early (to help with startup prefetch). + static const int kSlaveCountMin = 4; + + Lock lock_; + + // name_buffer_ holds a list of names we need to look up. + std::queue<std::string> name_buffer_; + + // results_ contains information progress for existing/prior prefetches. + Results results_; + + // Signaling slaves to process elements in the queue, or to terminate, + // is done using ConditionVariables. + ConditionVariable slaves_have_work_; + + int slave_count_; // Count of slave processes started. + int running_slave_count_; // Count of slaves process still running. + + // The following arrays are only initialized as + // slave_count_ grows (up to the indicated max). + DWORD thread_ids_[kSlaveCountMax]; + HANDLE thread_handles_[kSlaveCountMax]; + DnsSlave* slaves_[kSlaveCountMax]; + + // shutdown_ is set to tell the slaves to terminate. + bool shutdown_; + + // The following is the maximum time the ShutdownSlaves method + // will wait for all the slave processes to terminate. + const TimeDelta kShutdownWaitTime_; + + // A list of successful events resulting from pre-fetching. + DnsHostInfo::DnsInfoTable cache_hits_; + // A map of hosts that were evicted from our cache (after we prefetched them) + // and before the HTTP stack tried to look them up. + Results cache_eviction_map_; + + DISALLOW_EVIL_CONSTRUCTORS(DnsMaster); +}; + +} // namespace chrome_browser_net + +#endif // CHROME_BROWSER_NET_DNS_MASTER_H__ diff --git a/chrome/browser/net/dns_master_unittest.cc b/chrome/browser/net/dns_master_unittest.cc new file mode 100644 index 0000000..804af04 --- /dev/null +++ b/chrome/browser/net/dns_master_unittest.cc @@ -0,0 +1,451 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Multi-threaded tests of DnsMaster and DnsPrefetch slave functionality. + +#include <time.h> +#include <ws2tcpip.h> +#include <Wspiapi.h> // Needed for win2k compatibility + +#include <algorithm> +#include <map> +#include <sstream> +#include <string> + +#include "base/spin_wait.h" +#include "chrome/browser/net/dns_global.h" +#include "chrome/browser/net/dns_host_info.h" +#include "chrome/browser/net/dns_slave.h" +#include "net/base/winsock_init.h" +#include "testing/gtest/include/gtest/gtest.h" + + +namespace { + +class DnsMasterTest : public testing::Test { +}; + +typedef chrome_browser_net::DnsMaster DnsMaster; +typedef chrome_browser_net::DnsPrefetcherInit DnsPrefetcherInit; +typedef chrome_browser_net::DnsHostInfo DnsHostInfo; +typedef chrome_browser_net::NameList NameList; + + +//------------------------------------------------------------------------------ +// Provide network function stubs to run tests offline (and avoid the variance +// of real DNS lookups. +//------------------------------------------------------------------------------ + +static void __stdcall fake_free_addr_info(struct addrinfo* ai) { + // Kill off the dummy results. + EXPECT_TRUE(NULL != ai); + delete ai; +} + +static int __stdcall fake_get_addr_info(const char* nodename, + const char* servname, + const struct addrinfo* hints, + struct addrinfo** result) { + static Lock lock; + int duration; + bool was_found; + std::string hostname(nodename); + // Dummy up *some* return results to pass along. + *result = new addrinfo; + EXPECT_TRUE(NULL != *result); + { + AutoLock autolock(lock); + + static bool initialized = false; + typedef std::map<std::string, int> Latency; + static Latency latency; + static std::map<std::string, bool> found; + if (!initialized) { + initialized = true; + // List all known hostnames + latency["www.google.com"] = 50; + latency["gmail.google.com.com"] = 70; + latency["mail.google.com"] = 44; + latency["gmail.com"] = 63; + + for (Latency::iterator it = latency.begin(); latency.end() != it; it++) { + found[it->first] = true; + } + } // End static initialization + + was_found = found[hostname]; + + if (latency.end() != latency.find(hostname)) { + duration = latency[hostname]; + } else { + duration = 500; + } + // Change latency to simulate cache warming (next latency will be short). + latency[hostname] = 1; + } // Release lock. + + Sleep(duration); + + return was_found ? 0 : WSAHOST_NOT_FOUND; +} + +static void SetupNetworkInfrastructure() { + bool kUseFakeNetwork = true; + if (kUseFakeNetwork) + chrome_browser_net::SetAddrinfoCallbacks(fake_get_addr_info, + fake_free_addr_info); +} + +//------------------------------------------------------------------------------ +// Provide a function to create unique (nonexistant) domains at *every* call. +//------------------------------------------------------------------------------ +static std::string GetNonexistantDomain() { + static std::string postfix = ".google.com"; + static std::string prefix = "www."; + static std::string mid = "datecount"; + + static int counter = 0; // Make sure its unique. + time_t number = time(NULL); + std::ostringstream result; + result << prefix << number << mid << ++counter << postfix; + return result.str(); +} + +//------------------------------------------------------------------------------ +// Use a blocking function to contrast results we get via async services. +//------------------------------------------------------------------------------ +TimeDelta BlockingDnsLookup(const std::string& hostname) { + char* port = "80"; // I may need to get the real port + struct addrinfo* result = NULL; + Time start = Time::Now(); + + // Use the same underlying methods as dns_prefetch_slave does + chrome_browser_net::get_getaddrinfo()(hostname.c_str(), port, + NULL, &result); + + TimeDelta duration = Time::Now() - start; + + if (result) { + chrome_browser_net::get_freeaddrinfo()(result); + result = NULL; + } + + return duration; +} + +//------------------------------------------------------------------------------ + +// First test to be sure the OS is caching lookups, which is the whole premise +// of DNS prefetching. +TEST(DnsMasterTest, OsCachesLookupsTest) { + SetupNetworkInfrastructure(); + WinsockInit ws_init; + + for (int i = 0; i < 5; i++) { + std::string badname; + badname = GetNonexistantDomain(); + TimeDelta duration = BlockingDnsLookup(badname); + TimeDelta cached_duration = BlockingDnsLookup(badname); + EXPECT_TRUE(duration > cached_duration); + } +} + +TEST(DnsMasterTest, StartupShutdownTest) { + DnsMaster testing_master(TimeDelta::FromMilliseconds(5000)); + + // With no threads, we should have no problem doing a shutdown. + EXPECT_TRUE(testing_master.ShutdownSlaves()); +} + +TEST(DnsMasterTest, BenefitLookupTest) { + SetupNetworkInfrastructure(); + WinsockInit ws_init; + DnsPrefetcherInit dns_init(NULL); // Creates global service . + DnsMaster testing_master(TimeDelta::FromMilliseconds(5000)); + + std::string goog("www.google.com"), + goog2("gmail.google.com.com"), + goog3("mail.google.com"), + goog4("gmail.com"); + DnsHostInfo goog_info, goog2_info, goog3_info, goog4_info; + + // Simulate getting similar names from a network observer + goog_info.SetHostname(goog); + goog2_info.SetHostname(goog2); + goog3_info.SetHostname(goog3); + goog4_info.SetHostname(goog4); + + goog_info.SetStartedState(); + goog2_info.SetStartedState(); + goog3_info.SetStartedState(); + goog4_info.SetStartedState(); + + goog_info.SetFinishedState(true); + goog2_info.SetFinishedState(true); + goog3_info.SetFinishedState(true); + goog4_info.SetFinishedState(true); + + NameList names; + names.insert(names.end(), goog); + names.insert(names.end(), goog2); + names.insert(names.end(), goog3); + names.insert(names.end(), goog4); + + // First only cause a minimal set of threads to start up. + // Currently we actually start 4 threads when we get called with an array + testing_master.ResolveList(names); + + // Wait for some resoultion for each google. + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <= + testing_master.GetResolutionDuration(goog).InMilliseconds()); + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <= + testing_master.GetResolutionDuration(goog2).InMilliseconds()); + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <= + testing_master.GetResolutionDuration(goog3).InMilliseconds()); + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <= + testing_master.GetResolutionDuration(goog4).InMilliseconds()); + + EXPECT_EQ(std::min(names.size(), + 4u /* chrome_browser_net::DnsMaster::kSlaveCountMin */ ), + testing_master.running_slave_count()); + + EXPECT_TRUE(testing_master.WasFound(goog)); + EXPECT_TRUE(testing_master.WasFound(goog2)); + EXPECT_TRUE(testing_master.WasFound(goog3)); + EXPECT_TRUE(testing_master.WasFound(goog4)); + + // With the mock DNS, each of these should have taken some time, and hence + // shown a benefit (i.e., prefetch cost more than network access time). + + // Simulate actual navigation, and acrue the benefit for "helping" the DNS + // part of the navigation. + EXPECT_TRUE(testing_master.AcruePrefetchBenefits(&goog_info)); + EXPECT_TRUE(testing_master.AcruePrefetchBenefits(&goog2_info)); + EXPECT_TRUE(testing_master.AcruePrefetchBenefits(&goog3_info)); + EXPECT_TRUE(testing_master.AcruePrefetchBenefits(&goog4_info)); + + // Benefits can ONLY be reported once (for the first navigation). + EXPECT_FALSE(testing_master.AcruePrefetchBenefits(&goog_info)); + EXPECT_FALSE(testing_master.AcruePrefetchBenefits(&goog2_info)); + EXPECT_FALSE(testing_master.AcruePrefetchBenefits(&goog3_info)); + EXPECT_FALSE(testing_master.AcruePrefetchBenefits(&goog4_info)); + + // Ensure a clean shutdown. + EXPECT_TRUE(testing_master.ShutdownSlaves()); +} + +TEST(DnsMasterTest, DISABLED_SingleSlaveLookupTest) { + SetupNetworkInfrastructure(); + WinsockInit ws_init; + DnsPrefetcherInit dns_init(NULL); // Creates global service. + DnsMaster testing_master(TimeDelta::FromMilliseconds(5000)); + + std::string goog("www.google.com"), + goog2("gmail.google.com.com"), + goog3("mail.google.com"), + goog4("gmail.com"); + std::string bad1(GetNonexistantDomain()), + bad2(GetNonexistantDomain()); + + // Warm up local OS cache. + BlockingDnsLookup(goog); + + NameList names; + names.insert(names.end(), goog); + names.insert(names.end(), bad1); + names.insert(names.end(), bad2); + + // First only cause a single thread to start up + testing_master.ResolveList(names); + + // Wait for some resoultion for google. + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <= + testing_master.GetResolutionDuration(goog).InMilliseconds()); + + EXPECT_TRUE(testing_master.WasFound(goog)); + EXPECT_FALSE(testing_master.WasFound(bad1)); + EXPECT_FALSE(testing_master.WasFound(bad2)); + // Verify the reason it is not found is that it is still being proceessed. + // Negative time mean no resolution yet. + EXPECT_GT(0, testing_master.GetResolutionDuration(bad2).InMilliseconds()); + + // Spin long enough that we *do* find the resolution of bad2. + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <= + testing_master.GetResolutionDuration(bad2).InMilliseconds()); + + // Verify both fictitious names are resolved by now. + // Typical random name takes about 20-30 ms + EXPECT_LT(0, testing_master.GetResolutionDuration(bad1).InMilliseconds()); + EXPECT_LT(0, testing_master.GetResolutionDuration(bad2).InMilliseconds()); + EXPECT_FALSE(testing_master.WasFound(bad1)); + EXPECT_FALSE(testing_master.WasFound(bad2)); + + EXPECT_EQ(1, testing_master.running_slave_count()); + + // With just one thread (doing nothing now), ensure a clean shutdown. + EXPECT_TRUE(testing_master.ShutdownSlaves()); +} + +TEST(DnsMasterTest, DISABLED_MultiThreadedLookupTest) { + SetupNetworkInfrastructure(); + WinsockInit ws_init; + DnsMaster testing_master(TimeDelta::FromSeconds(30)); + DnsPrefetcherInit dns_init(NULL); + + std::string goog("www.google.com"), + goog2("gmail.google.com.com"), + goog3("mail.google.com"), + goog4("gmail.com"); + std::string bad1(GetNonexistantDomain()), + bad2(GetNonexistantDomain()); + + NameList names; + names.insert(names.end(), goog); + names.insert(names.end(), goog3); + names.insert(names.end(), bad1); + names.insert(names.end(), goog2); + names.insert(names.end(), bad2); + names.insert(names.end(), goog4); + names.insert(names.end(), goog); + + // Warm up the *OS* cache for all the goog domains. + BlockingDnsLookup(goog); + BlockingDnsLookup(goog2); + BlockingDnsLookup(goog3); + BlockingDnsLookup(goog4); + + // Get all 8 threads running by calling many times before queue is handled. + for (int i = 0; i < 10; i++) { + testing_master.ResolveList(names); + } + + Sleep(10); // Allow time for async DNS to get answers. + + EXPECT_TRUE(testing_master.WasFound(goog)); + EXPECT_TRUE(testing_master.WasFound(goog3)); + EXPECT_TRUE(testing_master.WasFound(goog2)); + EXPECT_TRUE(testing_master.WasFound(goog4)); + EXPECT_FALSE(testing_master.WasFound(bad1)); + EXPECT_FALSE(testing_master.WasFound(bad2)); + + EXPECT_EQ(8, testing_master.running_slave_count()); + + EXPECT_TRUE(testing_master.ShutdownSlaves()); +} + +TEST(DnsMasterTest, DISABLED_MultiThreadedSpeedupTest) { + SetupNetworkInfrastructure(); + WinsockInit ws_init; + DnsMaster testing_master(TimeDelta::FromSeconds(30)); + DnsPrefetcherInit dns_init(NULL); + + std::string goog("www.google.com"), + goog2("gmail.google.com.com"), + goog3("mail.google.com"), + goog4("gmail.com"); + std::string bad1(GetNonexistantDomain()), + bad2(GetNonexistantDomain()), + bad3(GetNonexistantDomain()), + bad4(GetNonexistantDomain()); + + NameList names; + names.insert(names.end(), goog); + names.insert(names.end(), bad1); + names.insert(names.end(), bad2); + names.insert(names.end(), goog3); + names.insert(names.end(), goog2); + names.insert(names.end(), bad3); + names.insert(names.end(), bad4); + names.insert(names.end(), goog4); + + // First cause a lookup using a single thread. + testing_master.ResolveList(names); + + // Wait for some resoultion for google. + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <= + testing_master.GetResolutionDuration(goog).InMilliseconds()); + + EXPECT_TRUE(testing_master.WasFound(goog)); + EXPECT_FALSE(testing_master.WasFound(bad1)); + EXPECT_FALSE(testing_master.WasFound(bad2)); + // ...and due to delay in geting resolution of bad names, the single slave + // thread won't have time to finish the list. + EXPECT_FALSE(testing_master.WasFound(goog3)); + EXPECT_FALSE(testing_master.WasFound(goog2)); + EXPECT_FALSE(testing_master.WasFound(goog4)); + + EXPECT_EQ(1, testing_master.running_slave_count()); + + // Get all 8 threads running by calling many times before queue is handled. + names.clear(); + for (int i = 0; i < 10; i++) + testing_master.Resolve(GetNonexistantDomain()); + + // Wait long enough for all the goog's to be resolved. + // They should all take about the same time, and run in parallel. + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <= + testing_master.GetResolutionDuration(goog2).InMilliseconds()); + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <= + testing_master.GetResolutionDuration(goog3).InMilliseconds()); + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <= + testing_master.GetResolutionDuration(goog4).InMilliseconds()); + + EXPECT_TRUE(testing_master.WasFound(goog3)); + EXPECT_TRUE(testing_master.WasFound(goog2)); + EXPECT_TRUE(testing_master.WasFound(goog4)); + + EXPECT_FALSE(testing_master.WasFound(bad1)); + EXPECT_FALSE(testing_master.WasFound(bad2)); // Perhaps not even decided. + + // Queue durations should be distinct from when 1 slave was working. + EXPECT_GT(testing_master.GetQueueDuration(goog3).InMilliseconds(), + testing_master.GetQueueDuration(goog).InMilliseconds()); + EXPECT_GT(testing_master.GetQueueDuration(goog4).InMilliseconds(), + testing_master.GetQueueDuration(goog).InMilliseconds()); + + // Give bad names a chance to be determined as unresolved. + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <= + testing_master.GetResolutionDuration(bad1).InMilliseconds()); + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <= + testing_master.GetResolutionDuration(bad2).InMilliseconds()); + + + // Well known names should resolve faster than bad names. + EXPECT_GE(testing_master.GetResolutionDuration(bad1).InMilliseconds(), + testing_master.GetResolutionDuration(goog).InMilliseconds()); + + EXPECT_GE(testing_master.GetResolutionDuration(bad2).InMilliseconds(), + testing_master.GetResolutionDuration(goog4).InMilliseconds()); + + EXPECT_EQ(8, testing_master.running_slave_count()); + + EXPECT_TRUE(testing_master.ShutdownSlaves()); +} + +} // namespace diff --git a/chrome/browser/net/dns_slave.cc b/chrome/browser/net/dns_slave.cc new file mode 100644 index 0000000..c3ec818 --- /dev/null +++ b/chrome/browser/net/dns_slave.cc @@ -0,0 +1,115 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// See header file for description of class + +#include <ws2tcpip.h> + +#include "chrome/browser/net/dns_slave.h" + +#include <Wspiapi.h> // Needed for win2k compatibility + +#include "base/logging.h" +#include "chrome/browser/net/dns_host_info.h" +#include "chrome/browser/net/dns_master.h" + + +namespace chrome_browser_net { + +//------------------------------------------------------------------------------ +// We supply a functions for stubbing network callbacks, so that offline testing +// can be performed. +//------------------------------------------------------------------------------ +static GetAddrInfoFunction get_addr = getaddrinfo; +static FreeAddrInfoFunction free_addr = freeaddrinfo; +void SetAddrinfoCallbacks(GetAddrInfoFunction getaddrinfo, + FreeAddrInfoFunction freeaddrinfo) { + get_addr = getaddrinfo; + free_addr = freeaddrinfo; +} + +GetAddrInfoFunction get_getaddrinfo() {return get_addr;} +FreeAddrInfoFunction get_freeaddrinfo() {return free_addr;} + + +//------------------------------------------------------------------------------ +// This is the entry method used by DnsMaster to start our thread. +//------------------------------------------------------------------------------ + +DWORD __stdcall DnsSlave::ThreadStart(void* pThis) { + DnsSlave* slave_instance = reinterpret_cast<DnsSlave*>(pThis); + return slave_instance->Run(); +} + +//------------------------------------------------------------------------------ +// The following are methods on the DnsPrefetch class. +//------------------------------------------------------------------------------ + +unsigned DnsSlave::Run() { + // We have to be running to set the thread name. + master_->SetSlaveName(slave_index_); + + while (master_->GetNextAssignment(&hostname_)) { + BlockingDnsLookup(); + } + // GetNextAssignment() fails when we are told to terminate. + master_->SetSlaveHasTerminated(slave_index_); + return 0; +} + +void DnsSlave::BlockingDnsLookup() { + const char* port = "80"; // I may need to get the real port + addrinfo* result = NULL; + + int error_code = get_addr(hostname_.c_str(), port, NULL, &result); + + // Note that since info has value semantics, I need to ask + // master_ to set the new states atomically in its map. + switch (error_code) { + case 0: + master_->SetFoundState(hostname_); + break; + + default: + DCHECK_EQ(0, error_code) << "surprising output" ; + // fall through + + case WSAHOST_NOT_FOUND: + master_->SetNoSuchNameState(hostname_); + break; + } + + // We don't save results, so lets free them... + if (result) { + free_addr(result); + result = NULL; + } +} + +} // namespace chrome_browser_net diff --git a/chrome/browser/net/dns_slave.h b/chrome/browser/net/dns_slave.h new file mode 100644 index 0000000..12c85ef --- /dev/null +++ b/chrome/browser/net/dns_slave.h @@ -0,0 +1,96 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A DnsSlave object processes hostname lookups +// via DNS on a single thread, waiting for that blocking +// call to complete, and then getting its next hostname +// from its associated DnsMaster object. +// Since this class only is concerned with prefetching +// to warm the underlying DNS cache, the actual IP +// does not need to be recorded. It is necessary to record +// when the lookup finished, so that the associated DnsMaster +// won't (wastefully) ask for the same name in "too short a +// period of time." +// This class does no "de-duping," and merely slavishly services +// items supplied by its DnsMaster. + +#ifndef CHROME_BROWSER_NET_DNS_SLAVE_H__ +#define CHROME_BROWSER_NET_DNS_SLAVE_H__ + +#include <windows.h> +#include <string> + +#include "base/basictypes.h" + +namespace chrome_browser_net { + +class DnsMaster; +class DnsSlave; + +// Support testing infrastructure, and allow off-line testing. +typedef void (__stdcall *FreeAddrInfoFunction)(struct addrinfo* ai); +typedef int (__stdcall *GetAddrInfoFunction)(const char* nodename, + const char* servname, + const struct addrinfo* hints, + struct addrinfo** result); +void SetAddrinfoCallbacks(GetAddrInfoFunction getaddrinfo, + FreeAddrInfoFunction freeaddrinfo); + +GetAddrInfoFunction get_getaddrinfo(); +FreeAddrInfoFunction get_freeaddrinfo(); + +class DnsSlave { + public: + DnsSlave(DnsMaster* master, int slave_index) + : slave_index_(slave_index), + master_(master) { + } + + ~DnsSlave() { + master_ = NULL; + } + + static DWORD WINAPI ThreadStart(void* pThis); + + unsigned Run(); + + private: + std::string hostname_; // Name being looked up. + + DnsMaster* master_; // Master that started us. + int slave_index_; // Our index into DnsMaster's array. + + void BlockingDnsLookup(); + + DISALLOW_EVIL_CONSTRUCTORS(DnsSlave); +}; + +} // namespace chrome_browser_net + +#endif // CHROME_BROWSER_NET_DNS_SLAVE_H__ |
