Index: chrome/browser/net/dns_master.cc |
diff --git a/chrome/browser/net/dns_master.cc b/chrome/browser/net/dns_master.cc |
index d58bb4bd251573b822f3ae4e70c6d85f976d8002..f2a584ec00b0eae3307610ced11a1d3c0e384d9f 100644 |
--- a/chrome/browser/net/dns_master.cc |
+++ b/chrome/browser/net/dns_master.cc |
@@ -2,61 +2,87 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-// See header file for description of class |
- |
#include "chrome/browser/net/dns_master.h" |
+#include <algorithm> |
#include <set> |
+#include <sstream> |
+#include "base/compiler_specific.h" |
#include "base/histogram.h" |
+#include "base/lock.h" |
+#include "base/ref_counted.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" |
- |
-using base::TimeDelta; |
+#include "base/time.h" |
+#include "net/base/address_list.h" |
+#include "net/base/completion_callback.h" |
+#include "net/base/host_resolver.h" |
+#include "net/base/net_errors.h" |
namespace chrome_browser_net { |
-DnsMaster::DnsMaster(TimeDelta shutdown_wait_time) |
- : slaves_have_work_(&lock_), |
- slave_count_(0), |
- running_slave_count_(0), |
- shutdown_(false), |
- kShutdownWaitTime_(shutdown_wait_time) { |
- for (size_t i = 0; i < kSlaveCountMax; i++) { |
- thread_ids_[i] = 0; |
- thread_handles_[i] = 0; |
- slaves_[i] = NULL; |
+// static |
+const size_t DnsMaster::kMaxConcurrentLookups = 8; |
+ |
+class DnsMaster::LookupRequest { |
+ public: |
+ LookupRequest(DnsMaster* master, const std::string& hostname) |
+ : ALLOW_THIS_IN_INITIALIZER_LIST( |
+ net_callback_(this, &LookupRequest::OnLookupFinished)), |
+ master_(master), |
+ hostname_(hostname) { |
} |
+ |
+ bool Start() { |
+ const int result = resolver_.Resolve(hostname_, 80, &addresses_, |
+ &net_callback_); |
+ return (result == net::ERR_IO_PENDING); |
+ } |
+ |
+ private: |
+ void OnLookupFinished(int result) { |
+ master_->OnLookupFinished(this, hostname_, result == net::OK); |
+ } |
+ |
+ // HostResolver will call us using this callback when resolution is complete. |
+ net::CompletionCallbackImpl<LookupRequest> net_callback_; |
+ |
+ DnsMaster* master_; // Master which started us. |
+ |
+ const std::string hostname_; // Hostname to resolve. |
+ net::HostResolver resolver_; |
+ net::AddressList addresses_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(LookupRequest); |
+}; |
+ |
+DnsMaster::DnsMaster() : peak_pending_lookups_(0), shutdown_(false) { |
+} |
+ |
+DnsMaster::~DnsMaster() { |
+ DCHECK(shutdown_); |
+} |
+ |
+void DnsMaster::Shutdown() { |
+ AutoLock auto_lock(lock_); |
+ |
+ DCHECK(!shutdown_); |
+ shutdown_ = true; |
+ |
+ std::set<LookupRequest*>::iterator it; |
+ for (it = pending_lookups_.begin(); it != pending_lookups_.end(); ++it) |
+ delete *it; |
} |
// Overloaded Resolve() to take a vector of names. |
void DnsMaster::ResolveList(const NameList& hostnames, |
DnsHostInfo::ResolutionMotivation motivation) { |
- bool need_to_signal = false; |
- { |
- AutoLock auto_lock(lock_); |
- if (shutdown_) return; |
- if (slave_count_ < kSlaveCountMin) { |
- for (int target_count = std::min(hostnames.size(), kSlaveCountMin); |
- target_count > 0; |
- target_count--) |
- PreLockedCreateNewSlaveIfNeeded(); |
- } else { |
- PreLockedCreateNewSlaveIfNeeded(); // Allocate one per list call. |
- } |
+ AutoLock auto_lock(lock_); |
- for (NameList::const_iterator it = hostnames.begin(); |
- it < hostnames.end(); |
- it++) { |
- if (PreLockedResolve(*it, motivation)) |
- need_to_signal = true; |
- } |
- } |
- if (need_to_signal) |
- slaves_have_work_.Signal(); |
+ NameList::const_iterator it; |
+ for (it = hostnames.begin(); it < hostnames.end(); ++it) |
+ PreLockedResolve(*it, motivation); |
} |
// Basic Resolve() takes an invidual name, and adds it |
@@ -65,16 +91,8 @@ void DnsMaster::Resolve(const std::string& hostname, |
DnsHostInfo::ResolutionMotivation motivation) { |
if (0 == hostname.length()) |
return; |
- bool need_to_signal = false; |
- { |
- AutoLock auto_lock(lock_); |
- if (shutdown_) return; |
- PreLockedCreateNewSlaveIfNeeded(); // Allocate one at a time. |
- if (PreLockedResolve(hostname, motivation)) |
- need_to_signal = true; |
- } |
- if (need_to_signal) |
- slaves_have_work_.Signal(); |
+ AutoLock auto_lock(lock_); |
+ PreLockedResolve(hostname, motivation); |
} |
bool DnsMaster::AccruePrefetchBenefits(const GURL& referrer, |
@@ -147,26 +165,19 @@ void DnsMaster::NonlinkNavigation(const GURL& referrer, |
} |
void DnsMaster::NavigatingTo(const std::string& host_name) { |
- bool need_to_signal = false; |
- { |
- AutoLock auto_lock(lock_); |
- Referrers::iterator it = referrers_.find(host_name); |
- if (referrers_.end() == it) |
- return; |
- Referrer* referrer = &(it->second); |
- for (Referrer::iterator future_host = referrer->begin(); |
- future_host != referrer->end(); ++future_host) { |
- DnsHostInfo* queued_info = PreLockedResolve( |
- future_host->first, |
- DnsHostInfo::LEARNED_REFERAL_MOTIVATED); |
- if (queued_info) { |
- need_to_signal = true; |
- queued_info->SetReferringHostname(host_name); |
- } |
- } |
+ AutoLock auto_lock(lock_); |
+ Referrers::iterator it = referrers_.find(host_name); |
+ if (referrers_.end() == it) |
+ return; |
+ Referrer* referrer = &(it->second); |
+ for (Referrer::iterator future_host = referrer->begin(); |
+ future_host != referrer->end(); ++future_host) { |
+ DnsHostInfo* queued_info = PreLockedResolve( |
+ future_host->first, |
+ DnsHostInfo::LEARNED_REFERAL_MOTIVATED); |
+ if (queued_info) |
+ queued_info->SetReferringHostname(host_name); |
} |
- if (need_to_signal) |
- slaves_have_work_.Signal(); |
} |
// Provide sort order so all .com's are together, etc. |
@@ -297,7 +308,7 @@ void DnsMaster::GetHtmlInfo(std::string* output) { |
} |
if (!it->second.was_found()) |
continue; // Still being processed. |
- if (TimeDelta() != it->second.benefits_remaining()) { |
+ if (base::TimeDelta() != it->second.benefits_remaining()) { |
network_hits.push_back(it->second); // With no benefit yet. |
continue; |
} |
@@ -332,9 +343,11 @@ DnsHostInfo* DnsMaster::PreLockedResolve( |
const std::string& hostname, |
DnsHostInfo::ResolutionMotivation motivation) { |
// DCHECK(We have the lock); |
- DCHECK(0 != slave_count_); |
DCHECK(0 != hostname.length()); |
+ if (shutdown_) |
+ return NULL; |
+ |
DnsHostInfo* info = &results_[hostname]; |
info->SetHostname(hostname); // Initialize or DCHECK. |
// TODO(jar): I need to discard names that have long since expired. |
@@ -349,151 +362,52 @@ DnsHostInfo* DnsMaster::PreLockedResolve( |
info->SetQueuedState(motivation); |
name_buffer_.push(hostname); |
+ |
+ PreLockedScheduleLookups(); |
+ |
return info; |
} |
-// 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(); |
+void DnsMaster::PreLockedScheduleLookups() { |
+ while (!name_buffer_.empty() && |
+ pending_lookups_.size() < kMaxConcurrentLookups) { |
+ const std::string hostname(name_buffer_.front()); |
name_buffer_.pop(); |
- DnsHostInfo* info = &results_[*hostname]; |
- DCHECK(info->HasHostname(*hostname)); |
+ 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(); |
+ LookupRequest* request = new LookupRequest(this, hostname); |
+ if (request->Start()) { |
+ pending_lookups_.insert(request); |
+ peak_pending_lookups_ = std::max(peak_pending_lookups_, |
+ pending_lookups_.size()); |
+ } else { |
+ NOTREACHED(); |
+ delete request; |
+ } |
+ } |
} |
-void DnsMaster::SetNoSuchNameState(const std::string hostname) { |
+void DnsMaster::OnLookupFinished(LookupRequest* request, |
+ const std::string& hostname, bool found) { |
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 than 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; |
+ else { |
+ if (found) |
+ info->SetFoundState(); |
+ else |
+ info->SetNoSuchNameState(); |
} |
- 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_++; |
+ pending_lookups_.erase(request); |
+ delete request; |
- 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(result); |
- thread_handles_[slave_count_] = 0; |
- delete slaves_[slave_count_]; |
- slaves_[slave_count_] = NULL; |
- } |
- } |
- } |
- return true; |
+ PreLockedScheduleLookups(); |
} |
void DnsMaster::DiscardAllResults() { |
@@ -514,10 +428,11 @@ void DnsMaster::DiscardAllResults() { |
info->SetAssignedState(); |
info->SetNoSuchNameState(); |
} |
- // Now every result_ is either resolved, or is being worked on by a slave. |
+ // Now every result_ is either resolved, or is being resolved |
+ // (see LookupRequest). |
// Step through result_, recording names of all hosts that can't be erased. |
- // We can't erase anything being worked on by a slave. |
+ // We can't erase anything being worked on. |
Results assignees; |
for (Results::iterator it = results_.begin(); results_.end() != it; ++it) { |
std::string hostname = it->first; |
@@ -528,9 +443,9 @@ void DnsMaster::DiscardAllResults() { |
assignees[hostname] = *info; |
} |
} |
- DCHECK(kSlaveCountMax >= assignees.size()); |
+ DCHECK(assignees.size() <= kMaxConcurrentLookups); |
results_.clear(); |
- // Put back in the names being worked on by slaves. |
+ // Put back in the names being worked on. |
for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) { |
DCHECK(it->second.is_marked_to_delete()); |
results_[it->first] = it->second; |
@@ -584,4 +499,3 @@ void DnsMaster::DeserializeReferrers(const ListValue& referral_list) { |
} |
} // namespace chrome_browser_net |
- |