diff options
author | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-01 22:28:20 +0000 |
---|---|---|
committer | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-01 22:28:20 +0000 |
commit | 0f292de0198f86ad901d0197f69bc74aac42137b (patch) | |
tree | d9b5f1704e64b20c02d78f8f9b323a95cb08df3f | |
parent | f5167883d8c59a17060f431048a115102c934be1 (diff) | |
download | chromium_src-0f292de0198f86ad901d0197f69bc74aac42137b.zip chromium_src-0f292de0198f86ad901d0197f69bc74aac42137b.tar.gz chromium_src-0f292de0198f86ad901d0197f69bc74aac42137b.tar.bz2 |
[net/dns] Refactoring of job dispatch in HostResolverImpl in preparation for DnsTransactionFactory.
- Replaces JobPool with PrioritizedDispatcher.
- Separates Job which aggregates Requests from ProcTask which executes on
WorkerPool.
- Queues Jobs rather than Requests before dispatching them.
(So that we can queue before DnsTransactionFactory and then before ProcTask.)
This is the second CL in series to merge AsyncHostResolver into
HostResolverImpl.
BUG=107880
TEST=waterfall
Review URL: http://codereview.chromium.org/9101011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@120114 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/net/connection_tester.cc | 10 | ||||
-rw-r--r-- | chrome/browser/net/passive_log_collector.cc | 54 | ||||
-rw-r--r-- | chrome/browser/net/passive_log_collector.h | 35 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/events_view.css | 11 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/source_entry.js | 3 | ||||
-rw-r--r-- | net/base/host_resolver.h | 8 | ||||
-rw-r--r-- | net/base/host_resolver_impl.cc | 1348 | ||||
-rw-r--r-- | net/base/host_resolver_impl.h | 270 | ||||
-rw-r--r-- | net/base/host_resolver_impl_unittest.cc | 146 | ||||
-rw-r--r-- | net/base/net_log_event_type_list.h | 86 | ||||
-rw-r--r-- | net/base/net_log_source_type_list.h | 24 |
11 files changed, 984 insertions, 1011 deletions
diff --git a/chrome/browser/net/connection_tester.cc b/chrome/browser/net/connection_tester.cc index 63af8d1..00e7482 100644 --- a/chrome/browser/net/connection_tester.cc +++ b/chrome/browser/net/connection_tester.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -17,7 +17,6 @@ #include "net/base/cert_verifier.h" #include "net/base/cookie_monster.h" #include "net/base/host_resolver.h" -#include "net/base/host_resolver_impl.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" @@ -110,9 +109,10 @@ class ExperimentURLRequestContext : public net::URLRequestContext { // Create a vanilla HostResolver that disables caching. const size_t kMaxJobs = 50u; const size_t kMaxRetryAttempts = 4u; - net::HostResolverImpl* impl = - new net::HostResolverImpl(NULL, NULL, kMaxJobs, kMaxRetryAttempts, - NULL); + net::HostResolver* impl = net::CreateNonCachingSystemHostResolver( + kMaxJobs, + kMaxRetryAttempts, + NULL /* NetLog */); host_resolver->reset(impl); diff --git a/chrome/browser/net/passive_log_collector.cc b/chrome/browser/net/passive_log_collector.cc index d7b93e0..9ca4b63 100644 --- a/chrome/browser/net/passive_log_collector.cc +++ b/chrome/browser/net/passive_log_collector.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -72,6 +72,8 @@ PassiveLogCollector::PassiveLogCollector() trackers_[net::NetLog::SOURCE_HOST_RESOLVER_IMPL_REQUEST] = &dns_request_tracker_; trackers_[net::NetLog::SOURCE_HOST_RESOLVER_IMPL_JOB] = &dns_job_tracker_; + trackers_[net::NetLog::SOURCE_HOST_RESOLVER_IMPL_PROC_TASK] = + &dns_proc_task_tracker_; trackers_[net::NetLog::SOURCE_DISK_CACHE_ENTRY] = &disk_cache_entry_tracker_; trackers_[net::NetLog::SOURCE_MEMORY_CACHE_ENTRY] = &mem_cache_entry_tracker_; trackers_[net::NetLog::SOURCE_HTTP_STREAM_JOB] = &http_stream_job_tracker_; @@ -536,18 +538,20 @@ PassiveLogCollector::SpdySessionTracker::DoAddEntry( } //---------------------------------------------------------------------------- -// DNSRequestTracker +// HostResolverRequestTracker //---------------------------------------------------------------------------- -const size_t PassiveLogCollector::DNSRequestTracker::kMaxNumSources = 200; -const size_t PassiveLogCollector::DNSRequestTracker::kMaxGraveyardSize = 20; +const size_t +PassiveLogCollector::HostResolverRequestTracker::kMaxNumSources = 200; +const size_t +PassiveLogCollector::HostResolverRequestTracker::kMaxGraveyardSize = 20; -PassiveLogCollector::DNSRequestTracker::DNSRequestTracker() +PassiveLogCollector::HostResolverRequestTracker::HostResolverRequestTracker() : SourceTracker(kMaxNumSources, kMaxGraveyardSize, NULL) { } PassiveLogCollector::SourceTracker::Action -PassiveLogCollector::DNSRequestTracker::DoAddEntry( +PassiveLogCollector::HostResolverRequestTracker::DoAddEntry( const ChromeNetLog::Entry& entry, SourceInfo* out_info) { AddEntryToSourceInfo(entry, out_info); if (entry.type == net::NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST && @@ -558,19 +562,21 @@ PassiveLogCollector::DNSRequestTracker::DoAddEntry( } //---------------------------------------------------------------------------- -// DNSJobTracker +// HostResolverJobTracker //---------------------------------------------------------------------------- -const size_t PassiveLogCollector::DNSJobTracker::kMaxNumSources = 100; -const size_t PassiveLogCollector::DNSJobTracker::kMaxGraveyardSize = 15; +const size_t +PassiveLogCollector::HostResolverJobTracker::kMaxNumSources = 100; +const size_t +PassiveLogCollector::HostResolverJobTracker::kMaxGraveyardSize = 15; -PassiveLogCollector::DNSJobTracker::DNSJobTracker() +PassiveLogCollector::HostResolverJobTracker::HostResolverJobTracker() : SourceTracker(kMaxNumSources, kMaxGraveyardSize, NULL) { } PassiveLogCollector::SourceTracker::Action -PassiveLogCollector::DNSJobTracker::DoAddEntry(const ChromeNetLog::Entry& entry, - SourceInfo* out_info) { +PassiveLogCollector::HostResolverJobTracker::DoAddEntry( + const ChromeNetLog::Entry& entry, SourceInfo* out_info) { AddEntryToSourceInfo(entry, out_info); if (entry.type == net::NetLog::TYPE_HOST_RESOLVER_IMPL_JOB && entry.phase == net::NetLog::PHASE_END) { @@ -580,6 +586,30 @@ PassiveLogCollector::DNSJobTracker::DoAddEntry(const ChromeNetLog::Entry& entry, } //---------------------------------------------------------------------------- +// HostResolverProcTaskTracker +//---------------------------------------------------------------------------- + +const size_t +PassiveLogCollector::HostResolverProcTaskTracker::kMaxNumSources = 100; +const size_t +PassiveLogCollector::HostResolverProcTaskTracker::kMaxGraveyardSize = 15; + +PassiveLogCollector::HostResolverProcTaskTracker::HostResolverProcTaskTracker() + : SourceTracker(kMaxNumSources, kMaxGraveyardSize, NULL) { +} + +PassiveLogCollector::SourceTracker::Action +PassiveLogCollector::HostResolverProcTaskTracker::DoAddEntry( + const ChromeNetLog::Entry& entry, SourceInfo* out_info) { + AddEntryToSourceInfo(entry, out_info); + if (entry.type == net::NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK && + entry.phase == net::NetLog::PHASE_END) { + return ACTION_MOVE_TO_GRAVEYARD; + } + return ACTION_NONE; +} + +//---------------------------------------------------------------------------- // DiskCacheEntryTracker //---------------------------------------------------------------------------- diff --git a/chrome/browser/net/passive_log_collector.h b/chrome/browser/net/passive_log_collector.h index b1a079a..2cb9ab4 100644 --- a/chrome/browser/net/passive_log_collector.h +++ b/chrome/browser/net/passive_log_collector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -268,33 +268,49 @@ class PassiveLogCollector : public ChromeNetLog::ThreadSafeObserverImpl { }; // Tracks the log entries for the last seen SOURCE_HOST_RESOLVER_IMPL_REQUEST. - class DNSRequestTracker : public SourceTracker { + class HostResolverRequestTracker : public SourceTracker { public: static const size_t kMaxNumSources; static const size_t kMaxGraveyardSize; - DNSRequestTracker(); + HostResolverRequestTracker(); private: virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info) OVERRIDE; - DISALLOW_COPY_AND_ASSIGN(DNSRequestTracker); + DISALLOW_COPY_AND_ASSIGN(HostResolverRequestTracker); }; // Tracks the log entries for the last seen SOURCE_HOST_RESOLVER_IMPL_JOB. - class DNSJobTracker : public SourceTracker { + class HostResolverJobTracker : public SourceTracker { public: static const size_t kMaxNumSources; static const size_t kMaxGraveyardSize; - DNSJobTracker(); + HostResolverJobTracker(); private: virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info) OVERRIDE; - DISALLOW_COPY_AND_ASSIGN(DNSJobTracker); + DISALLOW_COPY_AND_ASSIGN(HostResolverJobTracker); + }; + + // Tracks the log entries for the last seen + // SOURCE_HOST_RESOLVER_IMPL_PROC_TASK. + class HostResolverProcTaskTracker : public SourceTracker { + public: + static const size_t kMaxNumSources; + static const size_t kMaxGraveyardSize; + + HostResolverProcTaskTracker(); + + private: + virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, + SourceInfo* out_info) OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(HostResolverProcTaskTracker); }; // Tracks the log entries for the last seen SOURCE_DISK_CACHE_ENTRY. @@ -464,8 +480,9 @@ class PassiveLogCollector : public ChromeNetLog::ThreadSafeObserverImpl { RequestTracker socket_stream_tracker_; ProxyScriptDeciderTracker proxy_script_decider_tracker_; SpdySessionTracker spdy_session_tracker_; - DNSRequestTracker dns_request_tracker_; - DNSJobTracker dns_job_tracker_; + HostResolverRequestTracker dns_request_tracker_; + HostResolverJobTracker dns_job_tracker_; + HostResolverProcTaskTracker dns_proc_task_tracker_; DiskCacheEntryTracker disk_cache_entry_tracker_; MemCacheEntryTracker mem_cache_entry_tracker_; HttpStreamJobTracker http_stream_job_tracker_; diff --git a/chrome/browser/resources/net_internals/events_view.css b/chrome/browser/resources/net_internals/events_view.css index 914c72b..3b0c7bb 100644 --- a/chrome/browser/resources/net_internals/events_view.css +++ b/chrome/browser/resources/net_internals/events_view.css @@ -1,8 +1,8 @@ -/* -Copyright (c) 2011 The Chromium Authors. All rights reserved. -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. -*/ +/** + * Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #events-view-filter-box { background: #efefef; @@ -74,6 +74,7 @@ found in the LICENSE file. #events-view-source-list-tbody .source_HOST_RESOLVER_IMPL_JOB, #events-view-source-list-tbody .source_HOST_RESOLVER_IMPL_REQUEST, +#events-view-source-list-tbody .source_HOST_RESOLVER_IMPL_PROC_TASK, #events-view-source-list-tbody .source_ASYNC_HOST_RESOLVER_REQUEST, #events-view-source-list-tbody .source_DNS_TRANSACTION { color: #206060; diff --git a/chrome/browser/resources/net_internals/source_entry.js b/chrome/browser/resources/net_internals/source_entry.js index 1a244db..389369b 100644 --- a/chrome/browser/resources/net_internals/source_entry.js +++ b/chrome/browser/resources/net_internals/source_entry.js @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -88,6 +88,7 @@ var SourceEntry = (function() { break; case LogSourceType.HOST_RESOLVER_IMPL_REQUEST: case LogSourceType.HOST_RESOLVER_IMPL_JOB: + case LogSourceType.HOST_RESOLVER_IMPL_PROC_TASK: this.description_ = e.params.host; break; case LogSourceType.DISK_CACHE_ENTRY: diff --git a/net/base/host_resolver.h b/net/base/host_resolver.h index a596610..02b61af 100644 --- a/net/base/host_resolver.h +++ b/net/base/host_resolver.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -182,6 +182,12 @@ NET_EXPORT HostResolver* CreateSystemHostResolver( size_t max_retry_attempts, NetLog* net_log); +// As above, but the created HostResolver does not use a cache. +NET_EXPORT HostResolver* CreateNonCachingSystemHostResolver( + size_t max_concurrent_resolves, + size_t max_retry_attempts, + NetLog* net_log); + // Creates a HostResolver implementation that sends actual DNS queries to // the specified DNS server and parses response and returns results. NET_EXPORT HostResolver* CreateAsyncHostResolver(size_t max_concurrent_resolves, diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc index c5fc2e7..daf9b04 100644 --- a/net/base/host_resolver_impl.cc +++ b/net/base/host_resolver_impl.cc @@ -11,12 +11,13 @@ #endif #include <cmath> -#include <deque> +#include <utility> #include <vector> #include "base/basictypes.h" #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/callback.h" #include "base/compiler_specific.h" #include "base/debug/debugger.h" #include "base/debug/stack_trace.h" @@ -53,6 +54,12 @@ const size_t kMaxHostLength = 4096; // Default TTL for successful resolutions with ProcTask. const unsigned kCacheEntryTTLSeconds = 60; +// Maximum of 8 concurrent resolver threads (excluding retries). +// Some routers (or resolvers) appear to start to provide host-not-found if +// too many simultaneous resolutions are pending. This number needs to be +// further optimized, but 8 is what FF currently does. +static const size_t kDefaultMaxProcTasks = 8u; + // Helper to mutate the linked list contained by AddressList to the given // port. Note that in general this is dangerous since the AddressList's // data might be shared (and you should use AddressList::SetPort). @@ -129,44 +136,23 @@ std::vector<int> GetAllGetAddrinfoOSErrors() { arraysize(os_errors)); } -} // anonymous namespace - -// static -HostResolver* CreateSystemHostResolver(size_t max_concurrent_resolves, - size_t max_retry_attempts, - NetLog* net_log) { - // Maximum of 8 concurrent resolver threads. - // Some routers (or resolvers) appear to start to provide host-not-found if - // too many simultaneous resolutions are pending. This number needs to be - // further optimized, but 8 is what FF currently does. - static const size_t kDefaultMaxJobs = 8u; - - if (max_concurrent_resolves == HostResolver::kDefaultParallelism) - max_concurrent_resolves = kDefaultMaxJobs; - - HostResolverImpl* resolver = - new HostResolverImpl(NULL, HostCache::CreateDefaultCache(), - max_concurrent_resolves, max_retry_attempts, net_log); - - return resolver; -} - -static int ResolveAddrInfo(HostResolverProc* resolver_proc, - const std::string& host, - AddressFamily address_family, - HostResolverFlags host_resolver_flags, - AddressList* out, - int* os_error) { - if (resolver_proc) { - // Use the custom procedure. - return resolver_proc->Resolve(host, address_family, - host_resolver_flags, out, os_error); - } else { - // Use the system procedure (getaddrinfo). - return SystemHostResolverProc(host, address_family, - host_resolver_flags, out, os_error); +// Wraps call to SystemHostResolverProc as an instance of HostResolverProc. +// TODO(szym): This should probably be declared in host_resolver_proc.h. +class CallSystemHostResolverProc : public HostResolverProc { + public: + CallSystemHostResolverProc() : HostResolverProc(NULL) {} + virtual int Resolve(const std::string& hostname, + AddressFamily address_family, + HostResolverFlags host_resolver_flags, + AddressList* addrlist, + int* os_error) OVERRIDE { + return SystemHostResolverProc(hostname, + address_family, + host_resolver_flags, + addrlist, + os_error); } -} +}; // Extra parameters to attach to the NetLog when the resolve failed. class HostResolveFailedParams : public NetLog::EventParameters { @@ -179,7 +165,7 @@ class HostResolveFailedParams : public NetLog::EventParameters { os_error_(os_error) { } - virtual Value* ToValue() const { + virtual Value* ToValue() const OVERRIDE { DictionaryValue* dict = new DictionaryValue(); if (attempt_number_) dict->SetInteger("attempt_number", attempt_number_); @@ -223,7 +209,7 @@ class RequestInfoParameters : public NetLog::EventParameters { const NetLog::Source& source) : info_(info), source_(source) {} - virtual Value* ToValue() const { + virtual Value* ToValue() const OVERRIDE { DictionaryValue* dict = new DictionaryValue(); dict->SetString("host", info_.host_port_pair().ToString()); dict->SetInteger("address_family", @@ -243,13 +229,15 @@ class RequestInfoParameters : public NetLog::EventParameters { const NetLog::Source source_; }; -// Parameters associated with the creation of a HostResolverImpl::Job. +// Parameters associated with the creation of a HostResolverImpl::Job +// or a HostResolverImpl::ProcTask. class JobCreationParameters : public NetLog::EventParameters { public: - JobCreationParameters(const std::string& host, const NetLog::Source& source) + JobCreationParameters(const std::string& host, + const NetLog::Source& source) : host_(host), source_(source) {} - virtual Value* ToValue() const { + virtual Value* ToValue() const OVERRIDE { DictionaryValue* dict = new DictionaryValue(); dict->SetString("host", host_); dict->Set("source_dependency", source_.ToValue()); @@ -261,8 +249,164 @@ class JobCreationParameters : public NetLog::EventParameters { const NetLog::Source source_; }; +// Parameters of the HOST_RESOLVER_IMPL_JOB_ATTACH/DETACH event. +class JobAttachParameters : public NetLog::EventParameters { + public: + JobAttachParameters(const NetLog::Source& source, + RequestPriority priority) + : source_(source), priority_(priority) {} + + virtual Value* ToValue() const OVERRIDE { + DictionaryValue* dict = new DictionaryValue(); + dict->Set("source_dependency", source_.ToValue()); + dict->SetInteger("priority", priority_); + return dict; + } + + private: + const NetLog::Source source_; + const RequestPriority priority_; +}; + +// The logging routines are defined here because some requests are resolved +// without a Request object. + +// Logs when a request has just been started. +void LogStartRequest(const BoundNetLog& source_net_log, + const BoundNetLog& request_net_log, + const HostResolver::RequestInfo& info) { + source_net_log.BeginEvent( + NetLog::TYPE_HOST_RESOLVER_IMPL, + make_scoped_refptr(new NetLogSourceParameter( + "source_dependency", request_net_log.source()))); + + request_net_log.BeginEvent( + NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, + make_scoped_refptr(new RequestInfoParameters( + info, source_net_log.source()))); +} + +// Logs when a request has just completed (before its callback is run). +void LogFinishRequest(const BoundNetLog& source_net_log, + const BoundNetLog& request_net_log, + const HostResolver::RequestInfo& info, + int net_error, + int os_error) { + scoped_refptr<NetLog::EventParameters> params; + if (net_error != OK) { + params = new HostResolveFailedParams(0, net_error, os_error); + } + + request_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, params); + source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL, NULL); +} + +// Logs when a request has been cancelled. +void LogCancelRequest(const BoundNetLog& source_net_log, + const BoundNetLog& request_net_log, + const HostResolverImpl::RequestInfo& info) { + request_net_log.AddEvent(NetLog::TYPE_CANCELLED, NULL); + request_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, NULL); + source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL, NULL); +} + +//----------------------------------------------------------------------------- + +// Keeps track of the highest priority. +class PriorityTracker { + public: + PriorityTracker() + : highest_priority_(IDLE), total_count_(0) { + memset(counts_, 0, sizeof(counts_)); + } + + RequestPriority highest_priority() const { + return highest_priority_; + } + + size_t total_count() const { + return total_count_; + } + + void Add(RequestPriority req_priority) { + ++total_count_; + ++counts_[req_priority]; + if (highest_priority_ > req_priority) + highest_priority_ = req_priority; + } + + void Remove(RequestPriority req_priority) { + DCHECK_GT(total_count_, 0u); + DCHECK_GT(counts_[req_priority], 0u); + --total_count_; + --counts_[req_priority]; + size_t i; + for (i = highest_priority_; i < NUM_PRIORITIES && !counts_[i]; ++i); + highest_priority_ = static_cast<RequestPriority>(i); + + // In absence of requests set default. + if (highest_priority_ == NUM_PRIORITIES) { + DCHECK_EQ(0u, total_count_); + highest_priority_ = IDLE; + } + } + + private: + RequestPriority highest_priority_; + size_t total_count_; + size_t counts_[NUM_PRIORITIES]; +}; + //----------------------------------------------------------------------------- +HostResolver* CreateHostResolver(size_t max_concurrent_resolves, + size_t max_retry_attempts, + bool use_cache, + NetLog* net_log) { + if (max_concurrent_resolves == HostResolver::kDefaultParallelism) + max_concurrent_resolves = kDefaultMaxProcTasks; + + // TODO(szym): Add experiments with reserved slots for higher priority + // requests. + + PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, max_concurrent_resolves); + + HostResolverImpl* resolver = new HostResolverImpl( + use_cache ? HostCache::CreateDefaultCache() : NULL, + limits, + HostResolverImpl::ProcTaskParams(NULL, max_retry_attempts), + net_log); + + return resolver; +} + +} // anonymous namespace + +//----------------------------------------------------------------------------- + +HostResolver* CreateSystemHostResolver(size_t max_concurrent_resolves, + size_t max_retry_attempts, + NetLog* net_log) { + return CreateHostResolver(max_concurrent_resolves, + max_retry_attempts, + true /* use_cache */, + net_log); +} + +HostResolver* CreateNonCachingSystemHostResolver(size_t max_concurrent_resolves, + size_t max_retry_attempts, + NetLog* net_log) { + return CreateHostResolver(max_concurrent_resolves, + max_retry_attempts, + false /* use_cache */, + net_log); +} + +//----------------------------------------------------------------------------- + +// Holds the data for a request that could not be completed synchronously. +// It is owned by a Job. Canceled Requests are only marked as canceled rather +// than removed from the Job's |requests_| list. class HostResolverImpl::Request { public: Request(const BoundNetLog& source_net_log, @@ -278,43 +422,42 @@ class HostResolverImpl::Request { addresses_(addresses) { } - // Mark the request as cancelled. - void MarkAsCancelled() { + // Mark the request as canceled. + void MarkAsCanceled() { job_ = NULL; addresses_ = NULL; callback_.Reset(); } - bool was_cancelled() const { + bool was_canceled() const { return callback_.is_null(); } void set_job(Job* job) { - DCHECK(job != NULL); + DCHECK(job); // Identify which job the request is waiting on. job_ = job; } + // Prepare final AddressList and call completion callback. void OnComplete(int error, const AddressList& addrlist) { if (error == OK) - *addresses_ = CreateAddressListUsingPort(addrlist, port()); + *addresses_ = CreateAddressListUsingPort(addrlist, info_.port()); CompletionCallback callback = callback_; - MarkAsCancelled(); + MarkAsCanceled(); callback.Run(error); } - int port() const { - return info_.port(); - } - Job* job() const { return job_; } + // NetLog for the source, passed in HostResolver::Resolve. const BoundNetLog& source_net_log() { return source_net_log_; } + // NetLog for this request. const BoundNetLog& request_net_log() { return request_net_log_; } @@ -330,7 +473,7 @@ class HostResolverImpl::Request { // The request info that started the request. RequestInfo info_; - // The resolve job (running in worker pool) that this request is dependent on. + // The resolve job that this request is dependent on. Job* job_; // The user's callback to invoke when the request completes. @@ -349,56 +492,88 @@ class HostResolverImpl::Request { #define DNS_HISTOGRAM(name, time) UMA_HISTOGRAM_CUSTOM_TIMES(name, time, \ base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromHours(1), 100) -// This class represents a request to the worker pool for a "getaddrinfo()" -// call. -class HostResolverImpl::Job - : public base::RefCountedThreadSafe<HostResolverImpl::Job> { +// Calls HostResolverProc on the WorkerPool. Performs retries if necessary. +// +// Whenever we try to resolve the host, we post a delayed task to check if host +// resolution (OnLookupComplete) is completed or not. If the original attempt +// hasn't completed, then we start another attempt for host resolution. We take +// the results from the first attempt that finishes and ignore the results from +// all other attempts. +// +// TODO(szym): Move to separate source file for testing and mocking. +// +class HostResolverImpl::ProcTask + : public base::RefCountedThreadSafe<HostResolverImpl::ProcTask> { public: - Job(int id, - HostResolverImpl* resolver, - const Key& key, - const BoundNetLog& source_net_log, - NetLog* net_log) - : id_(id), - key_(key), - resolver_(resolver), - origin_loop_(base::MessageLoopProxy::current()), - resolver_proc_(resolver->effective_resolver_proc()), - unresponsive_delay_(resolver->unresponsive_delay()), - attempt_number_(0), - completed_attempt_number_(0), - completed_attempt_error_(ERR_UNEXPECTED), - had_non_speculative_request_(false), - net_log_(BoundNetLog::Make(net_log, - NetLog::SOURCE_HOST_RESOLVER_IMPL_JOB)) { - DCHECK(resolver); - net_log_.BeginEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, - make_scoped_refptr( - new JobCreationParameters(key.hostname, source_net_log.source()))); + typedef base::Callback<void(int, int, const AddressList&)> Callback; + + ProcTask(const Key& key, + const ProcTaskParams& params, + const Callback& callback, + const BoundNetLog& job_net_log) + : key_(key), + params_(params), + callback_(callback), + origin_loop_(base::MessageLoopProxy::current()), + attempt_number_(0), + completed_attempt_number_(0), + completed_attempt_error_(ERR_UNEXPECTED), + had_non_speculative_request_(false), + net_log_(BoundNetLog::Make( + job_net_log.net_log(), + NetLog::SOURCE_HOST_RESOLVER_IMPL_PROC_TASK)) { + if (!params_.resolver_proc) + params_.resolver_proc = HostResolverProc::GetDefault(); + // If default is unset, use the system proc. + if (!params_.resolver_proc) + params_.resolver_proc = new CallSystemHostResolverProc(); + + job_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CREATE_PROC_TASK, + new NetLogSourceParameter("source_dependency", + net_log_.source())); + + net_log_.BeginEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK, + new JobCreationParameters(key_.hostname, + job_net_log.source())); } - // Attaches a request to this job. The job takes ownership of |req| and will - // take care to delete it. - void AddRequest(Request* req) { + void Start() { DCHECK(origin_loop_->BelongsToCurrentThread()); - req->request_net_log().BeginEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_ATTACH, - make_scoped_refptr(new NetLogSourceParameter( - "source_dependency", net_log_.source()))); + StartLookupAttempt(); + } - req->set_job(this); - requests_.push_back(req); + // Cancels this ProcTask. It will be orphaned. Any outstanding resolve + // attempts running on worker threads will continue running. Only once all the + // attempts complete will the final reference to this ProcTask be released. + void Cancel() { + DCHECK(origin_loop_->BelongsToCurrentThread()); - if (!req->info().is_speculative()) - had_non_speculative_request_ = true; + if (was_canceled()) + return; + + net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL); + + callback_.Reset(); + + net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK, NULL); } - void Start() { + void set_had_non_speculative_request() { DCHECK(origin_loop_->BelongsToCurrentThread()); - StartLookupAttempt(); + had_non_speculative_request_ = true; + } + + bool was_canceled() const { + DCHECK(origin_loop_->BelongsToCurrentThread()); + return callback_.is_null(); + } + + bool was_completed() const { + DCHECK(origin_loop_->BelongsToCurrentThread()); + return completed_attempt_number_ > 0; } + private: void StartLookupAttempt() { DCHECK(origin_loop_->BelongsToCurrentThread()); base::TimeTicks start_time = base::TimeTicks::Now(); @@ -406,7 +581,7 @@ class HostResolverImpl::Job // Dispatch the lookup attempt to a worker thread. if (!base::WorkerPool::PostTask( FROM_HERE, - base::Bind(&Job::DoLookup, this, start_time, attempt_number_), + base::Bind(&ProcTask::DoLookup, this, start_time, attempt_number_), true)) { NOTREACHED(); @@ -415,7 +590,7 @@ class HostResolverImpl::Job // returned (IO_PENDING). origin_loop_->PostTask( FROM_HERE, - base::Bind(&Job::OnLookupComplete, this, AddressList(), + base::Bind(&ProcTask::OnLookupComplete, this, AddressList(), start_time, attempt_number_, ERR_UNEXPECTED, 0)); return; } @@ -425,88 +600,17 @@ class HostResolverImpl::Job make_scoped_refptr(new NetLogIntegerParameter( "attempt_number", attempt_number_))); - // Post a task to check if we get the results within a given time. - // OnCheckForComplete has the potential for starting a new attempt on a - // different worker thread if none of our outstanding attempts have - // completed yet. - if (attempt_number_ <= resolver_->max_retry_attempts()) { + // If we don't get the results within a given time, RetryIfNotComplete + // will start a new attempt on a different worker thread if none of our + // outstanding attempts have completed yet. + if (attempt_number_ <= params_.max_retry_attempts) { origin_loop_->PostDelayedTask( FROM_HERE, - base::Bind(&Job::OnCheckForComplete, this), - unresponsive_delay_.InMilliseconds()); + base::Bind(&ProcTask::RetryIfNotComplete, this), + params_.unresponsive_delay.InMilliseconds()); } } - // Cancels the current job. The Job will be orphaned. Any outstanding resolve - // attempts running on worker threads will continue running. Only once all the - // attempts complete will the final reference to this Job be released. - void Cancel() { - DCHECK(origin_loop_->BelongsToCurrentThread()); - net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL); - - HostResolver* resolver = resolver_; - resolver_ = NULL; - - // End here to prevent issues when a Job outlives the HostResolver that - // spawned it. - net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, NULL); - - // We will call HostResolverImpl::CancelRequest(Request*) on each one - // in order to notify any observers. - for (RequestsList::const_iterator it = requests_.begin(); - it != requests_.end(); ++it) { - HostResolverImpl::Request* req = *it; - if (!req->was_cancelled()) - resolver->CancelRequest(req); - } - } - - bool was_cancelled() const { - DCHECK(origin_loop_->BelongsToCurrentThread()); - return resolver_ == NULL; - } - - bool was_completed() const { - DCHECK(origin_loop_->BelongsToCurrentThread()); - return completed_attempt_number_ > 0; - } - - const Key& key() const { - DCHECK(origin_loop_->BelongsToCurrentThread()); - return key_; - } - - int id() const { - DCHECK(origin_loop_->BelongsToCurrentThread()); - return id_; - } - - const RequestsList& requests() const { - DCHECK(origin_loop_->BelongsToCurrentThread()); - return requests_; - } - - // Returns the first request attached to the job. - const Request* initial_request() const { - DCHECK(origin_loop_->BelongsToCurrentThread()); - DCHECK(!requests_.empty()); - return requests_[0]; - } - - // Returns true if |req_info| can be fulfilled by this job. - bool CanServiceRequest(const RequestInfo& req_info) const { - DCHECK(origin_loop_->BelongsToCurrentThread()); - return key_ == resolver_->GetEffectiveKeyForRequest(req_info); - } - - private: - friend class base::RefCountedThreadSafe<HostResolverImpl::Job>; - - ~Job() { - // Free the requests attached to this job. - STLDeleteElements(&requests_); - } - // WARNING: This code runs inside a worker pool. The shutdown code cannot // wait for it to finish, so we must be very careful here about using other // objects (like MessageLoops, Singletons, etc). During shutdown these objects @@ -517,28 +621,27 @@ class HostResolverImpl::Job AddressList results; int os_error = 0; // Running on the worker thread - int error = ResolveAddrInfo(resolver_proc_, - key_.hostname, - key_.address_family, - key_.host_resolver_flags, - &results, - &os_error); + + int error = params_.resolver_proc->Resolve(key_.hostname, + key_.address_family, + key_.host_resolver_flags, + &results, + &os_error); origin_loop_->PostTask( FROM_HERE, - base::Bind(&Job::OnLookupComplete, this, results, start_time, + base::Bind(&ProcTask::OnLookupComplete, this, results, start_time, attempt_number, error, os_error)); } - // Callback to see if DoLookup() has finished or not (runs on origin thread). - void OnCheckForComplete() { + // Makes next attempt if DoLookup() has not finished (runs on origin thread). + void RetryIfNotComplete() { DCHECK(origin_loop_->BelongsToCurrentThread()); - if (was_completed() || was_cancelled()) + if (was_completed() || was_canceled()) return; - DCHECK(resolver_); - unresponsive_delay_ *= resolver_->retry_factor(); + params_.unresponsive_delay *= params_.retry_factor; StartLookupAttempt(); } @@ -553,74 +656,53 @@ class HostResolverImpl::Job bool was_retry_attempt = attempt_number > 1; - if (!was_cancelled()) { - scoped_refptr<NetLog::EventParameters> params; - if (error != OK) { - params = new HostResolveFailedParams(attempt_number, error, os_error); - } else { - params = new NetLogIntegerParameter("attempt_number", attempt_number_); - } - net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_ATTEMPT_FINISHED, - params); - - // If host is already resolved, then record data and return. - if (was_completed()) { - // If this is the first attempt that is finishing later, then record - // data for the first attempt. Won't contaminate with retry attempt's - // data. - if (!was_retry_attempt) - RecordPerformanceHistograms(start_time, error, os_error); - - RecordAttemptHistograms(start_time, attempt_number, error, os_error); - return; - } - - // Copy the results from the first worker thread that resolves the host. - results_ = results; - completed_attempt_number_ = attempt_number; - completed_attempt_error_ = error; - } - // Ideally the following code would be part of host_resolver_proc.cc, // however it isn't safe to call NetworkChangeNotifier from worker // threads. So we do it here on the IO thread instead. if (error != OK && NetworkChangeNotifier::IsOffline()) error = ERR_INTERNET_DISCONNECTED; - // We will record data for the first attempt. Don't contaminate with retry - // attempt's data. + // If this is the first attempt that is finishing later, then record + // data for the first attempt. Won't contaminate with retry attempt's + // data. if (!was_retry_attempt) RecordPerformanceHistograms(start_time, error, os_error); RecordAttemptHistograms(start_time, attempt_number, error, os_error); - if (was_cancelled()) + if (was_canceled()) + return; + + scoped_refptr<NetLog::EventParameters> params; + if (error != OK) { + params = new HostResolveFailedParams(attempt_number, error, os_error); + } else { + params = new NetLogIntegerParameter("attempt_number", attempt_number_); + } + net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_ATTEMPT_FINISHED, params); + + if (was_completed()) return; + // Copy the results from the first worker thread that resolves the host. + results_ = results; + completed_attempt_number_ = attempt_number; + completed_attempt_error_ = error; + if (was_retry_attempt) { // If retry attempt finishes before 1st attempt, then get stats on how // much time is saved by having spawned an extra attempt. retry_attempt_finished_time_ = base::TimeTicks::Now(); } - scoped_refptr<NetLog::EventParameters> params; if (error != OK) { params = new HostResolveFailedParams(0, error, os_error); } else { params = new AddressListNetLogParam(results_); } + net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK, params); - // End here to prevent issues when a Job outlives the HostResolver that - // spawned it. - net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, params); - - DCHECK(!requests_.empty()); - - // Use the port number of the first request. - if (error == OK) - MutableSetPort(requests_[0]->port(), &results_); - - resolver_->OnJobComplete(this, error, os_error, results_); + callback_.Run(error, os_error, results_); } void RecordPerformanceHistograms(const base::TimeTicks& start_time, @@ -716,6 +798,7 @@ class HostResolverImpl::Job const uint32 attempt_number, const int error, const int os_error) const { + DCHECK(origin_loop_->BelongsToCurrentThread()); bool first_attempt_to_complete = completed_attempt_number_ == attempt_number; bool is_first_attempt = (attempt_number == 1); @@ -739,19 +822,19 @@ class HostResolverImpl::Job // If first attempt didn't finish before retry attempt, then calculate stats // on how much time is saved by having spawned an extra attempt. - if (!first_attempt_to_complete && is_first_attempt && !was_cancelled()) { + if (!first_attempt_to_complete && is_first_attempt && !was_canceled()) { DNS_HISTOGRAM("DNS.AttemptTimeSavedByRetry", base::TimeTicks::Now() - retry_attempt_finished_time_); } - if (was_cancelled() || !first_attempt_to_complete) { + if (was_canceled() || !first_attempt_to_complete) { // Count those attempts which completed after the job was already canceled // OR after the job was already completed by an earlier attempt (so in // effect). UMA_HISTOGRAM_ENUMERATION("DNS.AttemptDiscarded", attempt_number, 100); - // Record if job is cancelled. - if (was_cancelled()) + // Record if job is canceled. + if (was_canceled()) UMA_HISTOGRAM_ENUMERATION("DNS.AttemptCancelled", attempt_number, 100); } @@ -762,28 +845,20 @@ class HostResolverImpl::Job DNS_HISTOGRAM("DNS.AttemptFailDuration", duration); } - // Immutable. Can be read from either thread, - const int id_; - // Set on the origin thread, read on the worker thread. Key key_; - // Only used on the origin thread (where Resolve was called). - HostResolverImpl* resolver_; - RequestsList requests_; // The requests waiting on this job. - - // Used to post ourselves onto the origin thread. - scoped_refptr<base::MessageLoopProxy> origin_loop_; - - // Hold an owning reference to the HostResolverProc that we are going to use. + // Holds an owning reference to the HostResolverProc that we are going to use. // This may not be the current resolver procedure by the time we call // ResolveAddrInfo, but that's OK... we'll use it anyways, and the owning // reference ensures that it remains valid until we are done. - scoped_refptr<HostResolverProc> resolver_proc_; + ProcTaskParams params_; + + // The listener to the results of this ProcTask. + Callback callback_; - // The amount of time after starting a resolution attempt until deciding to - // retry. - base::TimeDelta unresponsive_delay_; + // Used to post ourselves onto the origin thread. + scoped_refptr<base::MessageLoopProxy> origin_loop_; // Keeps track of the number of attempts we have made so far to resolve the // host. Whenever we start an attempt to resolve the host, we increase this @@ -801,7 +876,7 @@ class HostResolverImpl::Job base::TimeTicks retry_attempt_finished_time_; // True if a non-speculative request was ever attached to this job - // (regardless of whether or not it was later cancelled. + // (regardless of whether or not it was later canceled. // This boolean is used for histogramming the duration of jobs used to // service non-speculative requests. bool had_non_speculative_request_; @@ -810,13 +885,12 @@ class HostResolverImpl::Job BoundNetLog net_log_; - DISALLOW_COPY_AND_ASSIGN(Job); + DISALLOW_COPY_AND_ASSIGN(ProcTask); }; //----------------------------------------------------------------------------- -// This class represents a request to the worker pool for a "probe for IPv6 -// support" call. +// Represents a request to the worker pool for a "probe for IPv6 support" call. class HostResolverImpl::IPv6ProbeJob : public base::RefCountedThreadSafe<HostResolverImpl::IPv6ProbeJob> { public: @@ -828,7 +902,7 @@ class HostResolverImpl::IPv6ProbeJob void Start() { DCHECK(origin_loop_->BelongsToCurrentThread()); - if (was_cancelled()) + if (was_canceled()) return; const bool kIsSlow = true; base::WorkerPool::PostTask( @@ -838,7 +912,7 @@ class HostResolverImpl::IPv6ProbeJob // Cancels the current job. void Cancel() { DCHECK(origin_loop_->BelongsToCurrentThread()); - if (was_cancelled()) + if (was_canceled()) return; resolver_ = NULL; // Read/write ONLY on origin thread. } @@ -849,7 +923,7 @@ class HostResolverImpl::IPv6ProbeJob ~IPv6ProbeJob() { } - bool was_cancelled() const { + bool was_canceled() const { DCHECK(origin_loop_->BelongsToCurrentThread()); return !resolver_; } @@ -868,7 +942,7 @@ class HostResolverImpl::IPv6ProbeJob // Callback for when DoProbe() completes. void OnProbeComplete(AddressFamily address_family) { DCHECK(origin_loop_->BelongsToCurrentThread()); - if (was_cancelled()) + if (was_canceled()) return; resolver_->IPv6ProbeSetDefaultAddressFamily(address_family); } @@ -884,199 +958,287 @@ class HostResolverImpl::IPv6ProbeJob //----------------------------------------------------------------------------- -// We rely on the priority enum values being sequential having starting at 0, -// and increasing for lower priorities. -COMPILE_ASSERT(HIGHEST == 0u && - LOWEST > HIGHEST && - IDLE > LOWEST && - NUM_PRIORITIES > IDLE, - priority_indexes_incompatible); - -// JobPool contains all the information relating to queued requests, including -// the limits on how many jobs are allowed to be used for this category of -// requests. -class HostResolverImpl::JobPool { +// Aggregates all Requests for the same Key. Dispatched via PriorityDispatch. +// Spawns ProcTask when started. +class HostResolverImpl::Job : public PrioritizedDispatcher::Job { public: - JobPool(size_t max_outstanding_jobs, size_t max_pending_requests) - : num_outstanding_jobs_(0u) { - SetConstraints(max_outstanding_jobs, max_pending_requests); - } - - ~JobPool() { - // Free the pending requests. - for (size_t i = 0; i < arraysize(pending_requests_); ++i) - STLDeleteElements(&pending_requests_[i]); - } - - // Sets the constraints for this pool. See SetPoolConstraints() for the - // specific meaning of these parameters. - void SetConstraints(size_t max_outstanding_jobs, - size_t max_pending_requests) { - CHECK_NE(max_outstanding_jobs, 0u); - max_outstanding_jobs_ = max_outstanding_jobs; - max_pending_requests_ = max_pending_requests; - } - - // Returns the number of pending requests enqueued to this pool. - // A pending request is one waiting to be attached to a job. - size_t GetNumPendingRequests() const { - size_t total = 0u; - for (size_t i = 0u; i < arraysize(pending_requests_); ++i) - total += pending_requests_[i].size(); - return total; - } - - bool HasPendingRequests() const { - return GetNumPendingRequests() > 0u; - } - - // Enqueues a request to this pool. As a result of enqueing this request, - // the queue may have reached its maximum size. In this case, a request is - // evicted from the queue, and returned. Otherwise returns NULL. The caller - // is responsible for freeing the evicted request. - Request* InsertPendingRequest(Request* req) { - req->request_net_log().BeginEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_POOL_QUEUE, - NULL); - - PendingRequestsQueue& q = pending_requests_[req->info().priority()]; - q.push_back(req); - - // If the queue is too big, kick out the lowest priority oldest request. - if (GetNumPendingRequests() > max_pending_requests_) { - // Iterate over the queues from lowest priority to highest priority. - for (int i = static_cast<int>(arraysize(pending_requests_)) - 1; - i >= 0; --i) { - PendingRequestsQueue& q = pending_requests_[i]; - if (!q.empty()) { - Request* req = q.front(); - q.pop_front(); - req->request_net_log().AddEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_POOL_QUEUE_EVICTED, NULL); - req->request_net_log().EndEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_POOL_QUEUE, NULL); - return req; - } + // Creates new job for |key| where |request_net_log| is bound to the + // request that spawned it. + Job(HostResolverImpl* resolver, + const Key& key, + const BoundNetLog& request_net_log) + : resolver_(resolver->AsWeakPtr()), + key_(key), + had_non_speculative_request_(false), + net_log_(BoundNetLog::Make(request_net_log.net_log(), + NetLog::SOURCE_HOST_RESOLVER_IMPL_JOB)), + net_error_(ERR_IO_PENDING), + os_error_(0) { + request_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CREATE_JOB, NULL); + + net_log_.BeginEvent( + NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, + make_scoped_refptr(new JobCreationParameters( + key_.hostname, request_net_log.source()))); + } + + virtual ~Job() { + if (net_error_ == ERR_IO_PENDING) { + if (is_running()) { + DCHECK_EQ(ERR_IO_PENDING, net_error_); + proc_task_->Cancel(); + proc_task_ = NULL; + net_error_ = ERR_ABORTED; + } else { + net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL); + net_error_ = OK; // For NetLog. + } + + for (RequestsList::const_iterator it = requests_.begin(); + it != requests_.end(); ++it) { + Request* req = *it; + if (req->was_canceled()) + continue; + DCHECK_EQ(this, req->job()); + LogCancelRequest(req->source_net_log(), req->request_net_log(), + req->info()); } } + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, + net_error_); + STLDeleteElements(&requests_); + } - return NULL; + HostResolverImpl* resolver() const { + return resolver_; } - // Erases |req| from this container. Caller is responsible for freeing - // |req| afterwards. - void RemovePendingRequest(Request* req) { - PendingRequestsQueue& q = pending_requests_[req->info().priority()]; - PendingRequestsQueue::iterator it = std::find(q.begin(), q.end(), req); - DCHECK(it != q.end()); - q.erase(it); - req->request_net_log().EndEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_POOL_QUEUE, NULL); + RequestPriority priority() const { + return priority_tracker_.highest_priority(); } - // Removes and returns the highest priority pending request. - Request* RemoveTopPendingRequest() { - DCHECK(HasPendingRequests()); + // Number of non-canceled requests in |requests_|. + size_t num_active_requests() const { + return priority_tracker_.total_count(); + } - for (size_t i = 0u; i < arraysize(pending_requests_); ++i) { - PendingRequestsQueue& q = pending_requests_[i]; - if (!q.empty()) { - Request* req = q.front(); - q.pop_front(); - req->request_net_log().EndEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_POOL_QUEUE, NULL); - return req; - } + const Key& key() const { + return key_; + } + + int net_error() const { + return net_error_; + } + + // Used by HostResolverImpl with |dispatcher_|. + const PrioritizedDispatcher::Handle& handle() const { + return handle_; + } + + void set_handle(const PrioritizedDispatcher::Handle& handle) { + handle_ = handle; + } + + // The Job will own |req| and destroy it in ~Job. + void AddRequest(Request* req) { + DCHECK_EQ(key_.hostname, req->info().hostname()); + + req->set_job(this); + requests_.push_back(req); + + priority_tracker_.Add(req->info().priority()); + + req->request_net_log().AddEvent( + NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_ATTACH, + make_scoped_refptr(new NetLogSourceParameter( + "source_dependency", net_log_.source()))); + + net_log_.AddEvent( + NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_ATTACH, + make_scoped_refptr(new JobAttachParameters( + req->request_net_log().source(), priority()))); + + // TODO(szym): Check if this is still needed. + if (!req->info().is_speculative()) { + had_non_speculative_request_ = true; + if (proc_task_) + proc_task_->set_had_non_speculative_request(); } + } - NOTREACHED(); - return NULL; + void CancelRequest(Request* req) { + DCHECK_EQ(key_.hostname, req->info().hostname()); + DCHECK(!req->was_canceled()); + // Don't remove it from |requests_| just mark it canceled. + req->MarkAsCanceled(); + LogCancelRequest(req->source_net_log(), req->request_net_log(), + req->info()); + priority_tracker_.Remove(req->info().priority()); + net_log_.AddEvent( + NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_DETACH, + make_scoped_refptr(new JobAttachParameters( + req->request_net_log().source(), priority()))); } - // Keeps track of a job that was just added/removed, and belongs to this pool. - void AdjustNumOutstandingJobs(int offset) { - DCHECK(offset == 1 || (offset == -1 && num_outstanding_jobs_ > 0u)); - num_outstanding_jobs_ += offset; + // Aborts and destroys the job, completes all requests as aborted. + void Abort() { + // Job should only be aborted if it's running. + DCHECK(is_running()); + proc_task_->Cancel(); + proc_task_ = NULL; + net_error_ = ERR_ABORTED; + os_error_ = 0; + CompleteRequests(AddressList()); } - void ResetNumOutstandingJobs() { - num_outstanding_jobs_ = 0; + bool is_running() const { + return proc_task_.get() != NULL; } - // Returns true if a new job can be created for this pool. - bool CanCreateJob() const { - return num_outstanding_jobs_ + 1u <= max_outstanding_jobs_; + // Called by HostResolverImpl when this job is evicted due to queue overflow. + void OnEvicted() { + // Must not be running. + DCHECK(!is_running()); + handle_ = PrioritizedDispatcher::Handle(); + + net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_EVICTED, NULL); + + // This signals to CompleteRequests that this job never ran. + net_error_ = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE; + os_error_ = 0; + CompleteRequests(AddressList()); } - // Removes any pending requests from the queue which are for the - // same (hostname / effective address-family) as |job|, and attaches them to - // |job|. - void MoveRequestsToJob(Job* job) { - for (size_t i = 0u; i < arraysize(pending_requests_); ++i) { - PendingRequestsQueue& q = pending_requests_[i]; - PendingRequestsQueue::iterator req_it = q.begin(); - while (req_it != q.end()) { - Request* req = *req_it; - if (job->CanServiceRequest(req->info())) { - // Job takes ownership of |req|. - job->AddRequest(req); - req_it = q.erase(req_it); - } else { - ++req_it; - } - } - } + // PriorityDispatch::Job interface. + virtual void Start() OVERRIDE { + DCHECK(!is_running()); + handle_ = PrioritizedDispatcher::Handle(); + + net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_STARTED, NULL); + + proc_task_ = new ProcTask( + key_, + resolver_->proc_params_, + base::Bind(&Job::OnProcTaskComplete, base::Unretained(this)), + net_log_); + + if (had_non_speculative_request_) + proc_task_->set_had_non_speculative_request(); + // Start() could be called from within Resolve(), hence it must NOT directly + // call OnProcTaskComplete, for example, on synchronous failure. + proc_task_->Start(); } private: - typedef std::deque<Request*> PendingRequestsQueue; + // Called by ProcTask when it completes. + void OnProcTaskComplete(int net_error, int os_error, + const AddressList& addrlist) { + DCHECK(is_running()); + proc_task_ = NULL; + net_error_ = net_error; + os_error_ = os_error; + + // We are the only consumer of |addrlist|, so we can safely change the port + // without copy-on-write. This pays off, when job has only one request. + AddressList list = addrlist; + if (net_error == OK && !requests_.empty()) + MutableSetPort(requests_.front()->info().port(), &list); + CompleteRequests(list); + } + + // Completes all Requests. Calls OnJobFinished and deletes self. + void CompleteRequests(const AddressList& addrlist) { + CHECK(resolver_); + + // This job must be removed from resolver's |jobs_| now to make room for a + // new job with the same key in case one of the OnComplete callbacks decides + // to spawn one. Consequently, the job deletes itself when CompleteRequests + // is done. + scoped_ptr<Job> self_deleter(this); + resolver_->OnJobFinished(this, addrlist); + + // Complete all of the requests that were attached to the job. + for (RequestsList::const_iterator it = requests_.begin(); + it != requests_.end(); ++it) { + Request* req = *it; + + if (req->was_canceled()) + continue; + + DCHECK_EQ(this, req->job()); + // Update the net log and notify registered observers. + LogFinishRequest(req->source_net_log(), req->request_net_log(), + req->info(), net_error_, os_error_); + + req->OnComplete(net_error_, addrlist); + + // Check if the resolver was destroyed as a result of running the + // callback. If it was, we could continue, but we choose to bail. + if (!resolver_) + return; + } + } + + // Used to call OnJobFinished and RemoveJob. + base::WeakPtr<HostResolverImpl> resolver_; - // Maximum number of concurrent jobs allowed to be started for requests - // belonging to this pool. - size_t max_outstanding_jobs_; + Key key_; + + // Tracks the highest priority across |requests_|. + PriorityTracker priority_tracker_; + + bool had_non_speculative_request_; + + BoundNetLog net_log_; - // The current number of running jobs that were started for requests - // belonging to this pool. - size_t num_outstanding_jobs_; + // Store result here in case the job fails fast in Resolve(). + int net_error_; + int os_error_; - // The maximum number of requests we allow to be waiting on a job, - // for this pool. - size_t max_pending_requests_; + // A ProcTask created and started when this Job is dispatched.. + scoped_refptr<ProcTask> proc_task_; - // The requests which are waiting to be started for this pool. - PendingRequestsQueue pending_requests_[NUM_PRIORITIES]; + // All Requests waiting for the result of this Job. Some can be canceled. + RequestsList requests_; + + // A handle used by HostResolverImpl in |dispatcher_|. + PrioritizedDispatcher::Handle handle_; }; //----------------------------------------------------------------------------- -HostResolverImpl::HostResolverImpl( +HostResolverImpl::ProcTaskParams::ProcTaskParams( HostResolverProc* resolver_proc, + size_t max_retry_attempts) + : resolver_proc(resolver_proc), + max_retry_attempts(max_retry_attempts), + unresponsive_delay(base::TimeDelta::FromMilliseconds(6000)), + retry_factor(2) { +} + +HostResolverImpl::ProcTaskParams::~ProcTaskParams() {} + +HostResolverImpl::HostResolverImpl( HostCache* cache, - size_t max_jobs, - size_t max_retry_attempts, + const PrioritizedDispatcher::Limits& job_limits, + const ProcTaskParams& proc_params, NetLog* net_log) : cache_(cache), - max_jobs_(max_jobs), - max_retry_attempts_(max_retry_attempts), - unresponsive_delay_(base::TimeDelta::FromMilliseconds(6000)), - retry_factor_(2), - next_job_id_(0), - resolver_proc_(resolver_proc), + dispatcher_(job_limits), + max_queued_jobs_(job_limits.total_jobs * 100u), + proc_params_(proc_params), default_address_family_(ADDRESS_FAMILY_UNSPECIFIED), ipv6_probe_monitoring_(false), additional_resolver_flags_(0), net_log_(net_log) { - DCHECK_GT(max_jobs, 0u); + + DCHECK_GE(dispatcher_.num_priorities(), static_cast<size_t>(NUM_PRIORITIES)); // Maximum of 4 retry attempts for host resolution. static const size_t kDefaultMaxRetryAttempts = 4u; - if (max_retry_attempts_ == HostResolver::kDefaultRetryAttempts) - max_retry_attempts_ = kDefaultMaxRetryAttempts; - - // It is cumbersome to expose all of the constraints in the constructor, - // so we choose some defaults, which users can override later. - job_pools_[POOL_NORMAL] = new JobPool(max_jobs, 100u * max_jobs); + if (proc_params_.max_retry_attempts == HostResolver::kDefaultRetryAttempts) + proc_params_.max_retry_attempts = kDefaultMaxRetryAttempts; #if defined(OS_WIN) EnsureWinsockInit(); @@ -1095,35 +1257,21 @@ HostResolverImpl::HostResolverImpl( } HostResolverImpl::~HostResolverImpl() { - // Cancel the outstanding jobs. Those jobs may contain several attached - // requests, which will also be cancelled. DiscardIPv6ProbeJob(); - CancelAllJobs(); - - // In case we are being deleted during the processing of a callback. - if (cur_completing_job_) - cur_completing_job_->Cancel(); + // This will also cancel all outstanding requests. + STLDeleteValues(&jobs_); NetworkChangeNotifier::RemoveIPAddressObserver(this); #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) NetworkChangeNotifier::RemoveDNSObserver(this); #endif - - // Delete the job pools. - for (size_t i = 0u; i < arraysize(job_pools_); ++i) - delete job_pools_[i]; } -void HostResolverImpl::SetPoolConstraints(JobPoolIndex pool_index, - size_t max_outstanding_jobs, - size_t max_pending_requests) { - DCHECK(CalledOnValidThread()); - CHECK_GE(pool_index, 0); - CHECK_LT(pool_index, POOL_COUNT); - CHECK(jobs_.empty()) << "Can only set constraints during setup"; - JobPool* pool = job_pools_[pool_index]; - pool->SetConstraints(max_outstanding_jobs, max_pending_requests); +void HostResolverImpl::SetMaxQueuedJobs(size_t value) { + DCHECK_EQ(0u, dispatcher_.num_queued_jobs()); + DCHECK_GT(value, 0u); + max_queued_jobs_ = value; } int HostResolverImpl::Resolve(const RequestInfo& info, @@ -1139,8 +1287,7 @@ int HostResolverImpl::Resolve(const RequestInfo& info, BoundNetLog request_net_log = BoundNetLog::Make(net_log_, NetLog::SOURCE_HOST_RESOLVER_IMPL_REQUEST); - // Update the net log and notify registered observers. - OnStartRequest(source_net_log, request_net_log, info); + LogStartRequest(source_net_log, request_net_log, info); // Build a key that identifies the request in the cache and in the // outstanding jobs map. @@ -1148,38 +1295,50 @@ int HostResolverImpl::Resolve(const RequestInfo& info, int rv = ResolveHelper(key, info, addresses, request_net_log); if (rv != ERR_DNS_CACHE_MISS) { - OnFinishRequest(source_net_log, request_net_log, info, - rv, - 0 /* os_error (unknown since from cache) */); + LogFinishRequest(source_net_log, request_net_log, info, rv, + 0 /* os_error (unknown since from cache) */); return rv; } - // Create a handle for this request, and pass it back to the user if they - // asked for it (out_req != NULL). - Request* req = new Request(source_net_log, request_net_log, info, - callback, addresses); - if (out_req) - *out_req = reinterpret_cast<RequestHandle>(req); - // Next we need to attach our request to a "job". This job is responsible for // calling "getaddrinfo(hostname)" on a worker thread. - scoped_refptr<Job> job; - // If there is already an outstanding job to resolve |key|, use - // it. This prevents starting concurrent resolves for the same hostname. - job = FindOutstandingJob(key); - if (job) { - job->AddRequest(req); - } else { - JobPool* pool = GetPoolForRequest(req); - if (CanCreateJobForPool(*pool)) { - CreateAndStartJob(req); - } else { - return EnqueueRequest(pool, req); + JobMap::iterator jobit = jobs_.find(key); + Job* job; + if (jobit == jobs_.end()) { + // Create new Job. + job = new Job(this, key, request_net_log); + job->set_handle(dispatcher_.Add(job, info.priority())); + + // Check for queue overflow. + if (dispatcher_.num_queued_jobs() > max_queued_jobs_) { + Job* evicted = static_cast<Job*>(dispatcher_.EvictOldestLowest()); + DCHECK(evicted); + if (evicted == job) { + delete job; + rv = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE; + LogFinishRequest(source_net_log, request_net_log, info, rv, 0); + return rv; + } + evicted->OnEvicted(); // Deletes |evicted|. } + + jobs_.insert(jobit, std::make_pair(key, job)); + } else { + job = jobit->second; } - // Completion happens during OnJobComplete(Job*). + // Can't complete synchronously. Create and attach request. + Request* req = new Request(source_net_log, request_net_log, info, callback, + addresses); + job->AddRequest(req); + if (!job->handle().is_null()) + job->set_handle(dispatcher_.ChangePriority(job->handle(), job->priority())); + if (out_req) + *out_req = reinterpret_cast<RequestHandle>(req); + + DCHECK_EQ(ERR_IO_PENDING, job->net_error()); + // Completion happens during Job::CompleteRequests(). return ERR_IO_PENDING; } @@ -1212,44 +1371,44 @@ int HostResolverImpl::ResolveFromCache(const RequestInfo& info, NetLog::SOURCE_HOST_RESOLVER_IMPL_REQUEST); // Update the net log and notify registered observers. - OnStartRequest(source_net_log, request_net_log, info); + LogStartRequest(source_net_log, request_net_log, info); - // Build a key that identifies the request in the cache and in the - // outstanding jobs map. Key key = GetEffectiveKeyForRequest(info); int rv = ResolveHelper(key, info, addresses, request_net_log); - OnFinishRequest(source_net_log, request_net_log, info, - rv, - 0 /* os_error (unknown since from cache) */); + LogFinishRequest(source_net_log, request_net_log, info, rv, + 0 /* os_error (unknown since from cache) */); return rv; } -// See OnJobComplete(Job*) for why it is important not to clean out -// cancelled requests from Job::requests_. void HostResolverImpl::CancelRequest(RequestHandle req_handle) { DCHECK(CalledOnValidThread()); Request* req = reinterpret_cast<Request*>(req_handle); DCHECK(req); - scoped_ptr<Request> request_deleter; // Frees at end of function. + Job* job = req->job(); + DCHECK(job); + + job->CancelRequest(req); - if (!req->job()) { - // If the request was not attached to a job yet, it must have been - // enqueued into a pool. Remove it from that pool's queue. - // Otherwise if it was attached to a job, the job is responsible for - // deleting it. - JobPool* pool = GetPoolForRequest(req); - pool->RemovePendingRequest(req); - request_deleter.reset(req); + if (!job->handle().is_null()) { + // Still in queue. + if (job->num_active_requests()) { + job->set_handle(dispatcher_.ChangePriority(job->handle(), + job->priority())); + } else { + dispatcher_.Cancel(job->handle()); + RemoveJob(job); + delete job; + } } else { - req->request_net_log().EndEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_ATTACH, NULL); + // Job is running (and could be in CompleteRequests right now). + // But to be in Request::OnComplete we would have to have a non-canceled + // request. So it is safe to Abort it if it has no more active requests. + if (!job->num_active_requests()) { + job->Abort(); + } } - - // NULL out the fields of req, to mark it as cancelled. - req->MarkAsCancelled(); - OnCancelRequest(req->source_net_log(), req->request_net_log(), req->info()); } void HostResolverImpl::SetDefaultAddressFamily(AddressFamily address_family) { @@ -1288,10 +1447,10 @@ bool HostResolverImpl::ResolveAsIP(const Key& key, ~(HOST_RESOLVER_CANONNAME | HOST_RESOLVER_LOOPBACK_ONLY | HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6), 0) << " Unhandled flag"; - bool ipv6_disabled = default_address_family_ == ADDRESS_FAMILY_IPV4 && - !ipv6_probe_monitoring_; + bool ipv6_disabled = (default_address_family_ == ADDRESS_FAMILY_IPV4) && + !ipv6_probe_monitoring_; *net_error = OK; - if (ip_number.size() == 16 && ipv6_disabled) { + if ((ip_number.size() == kIPv6AddressSize) && ipv6_disabled) { *net_error = ERR_NAME_NOT_RESOLVED; } else { *addresses = AddressList::CreateFromIPAddressWithCname( @@ -1323,129 +1482,30 @@ bool HostResolverImpl::ServeFromCache(const Key& key, return true; } -void HostResolverImpl::AddOutstandingJob(Job* job) { - scoped_refptr<Job>& found_job = jobs_[job->key()]; - DCHECK(!found_job); - found_job = job; - - JobPool* pool = GetPoolForRequest(job->initial_request()); - pool->AdjustNumOutstandingJobs(1); -} - -HostResolverImpl::Job* HostResolverImpl::FindOutstandingJob(const Key& key) { - JobMap::iterator it = jobs_.find(key); - if (it != jobs_.end()) - return it->second; - return NULL; -} - -void HostResolverImpl::RemoveOutstandingJob(Job* job) { - JobMap::iterator it = jobs_.find(job->key()); - DCHECK(it != jobs_.end()); - DCHECK_EQ(it->second.get(), job); - jobs_.erase(it); - - JobPool* pool = GetPoolForRequest(job->initial_request()); - pool->AdjustNumOutstandingJobs(-1); -} - -void HostResolverImpl::OnJobComplete(Job* job, - int net_error, - int os_error, - const AddressList& addrlist) { - RemoveOutstandingJob(job); +void HostResolverImpl::OnJobFinished(Job* job, const AddressList& addrlist) { + DCHECK(job); + DCHECK(job->handle().is_null()); + RemoveJob(job); + if (job->net_error() == ERR_HOST_RESOLVER_QUEUE_TOO_LARGE) + return; + // Signal dispatcher that a slot has opened. + dispatcher_.OnJobFinished(); + if (job->net_error() == ERR_ABORTED) + return; // Write result to the cache. if (cache_.get()) { base::TimeDelta ttl = base::TimeDelta::FromSeconds(0); - if (net_error == OK) + if (job->net_error() == OK) ttl = base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds); - cache_->Set(job->key(), net_error, addrlist, - base::TimeTicks::Now(), - ttl); - } - OnJobCompleteInternal(job, net_error, os_error, addrlist); -} - -void HostResolverImpl::AbortJob(Job* job) { - OnJobCompleteInternal(job, ERR_ABORTED, 0 /* no os_error */, AddressList()); -} - -void HostResolverImpl::OnJobCompleteInternal( - Job* job, - int net_error, - int os_error, - const AddressList& addrlist) { - // Make a note that we are executing within OnJobComplete() in case the - // HostResolver is deleted by a callback invocation. - DCHECK(!cur_completing_job_); - cur_completing_job_ = job; - - // Try to start any queued requests now that a job-slot has freed up. - ProcessQueuedRequests(); - - // Complete all of the requests that were attached to the job. - for (RequestsList::const_iterator it = job->requests().begin(); - it != job->requests().end(); ++it) { - Request* req = *it; - if (!req->was_cancelled()) { - DCHECK_EQ(job, req->job()); - req->request_net_log().EndEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_ATTACH, NULL); - - // Update the net log and notify registered observers. - OnFinishRequest(req->source_net_log(), req->request_net_log(), - req->info(), net_error, os_error); - - req->OnComplete(net_error, addrlist); - - // Check if the job was cancelled as a result of running the callback. - // (Meaning that |this| was deleted). - if (job->was_cancelled()) - return; - } + cache_->Set(job->key(), job->net_error(), addrlist, + base::TimeTicks::Now(), ttl); } - - cur_completing_job_ = NULL; } -void HostResolverImpl::OnStartRequest(const BoundNetLog& source_net_log, - const BoundNetLog& request_net_log, - const RequestInfo& info) { - source_net_log.BeginEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL, - make_scoped_refptr(new NetLogSourceParameter( - "source_dependency", request_net_log.source()))); - - request_net_log.BeginEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, - make_scoped_refptr(new RequestInfoParameters( - info, source_net_log.source()))); -} - -void HostResolverImpl::OnFinishRequest(const BoundNetLog& source_net_log, - const BoundNetLog& request_net_log, - const RequestInfo& info, - int net_error, - int os_error) { - bool was_resolved = net_error == OK; - - // Log some extra parameters on failure for synchronous requests. - scoped_refptr<NetLog::EventParameters> params; - if (!was_resolved) { - params = new HostResolveFailedParams(0, net_error, os_error); - } - - request_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, params); - source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL, NULL); -} - -void HostResolverImpl::OnCancelRequest(const BoundNetLog& source_net_log, - const BoundNetLog& request_net_log, - const RequestInfo& info) { - request_net_log.AddEvent(NetLog::TYPE_CANCELLED, NULL); - request_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, NULL); - source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL, NULL); +void HostResolverImpl::RemoveJob(Job* job) { + DCHECK(job); + jobs_.erase(job->key()); } void HostResolverImpl::DiscardIPv6ProbeJob() { @@ -1469,46 +1529,6 @@ void HostResolverImpl::IPv6ProbeSetDefaultAddressFamily( DiscardIPv6ProbeJob(); } -bool HostResolverImpl::CanCreateJobForPool(const JobPool& pool) const { - DCHECK_LE(jobs_.size(), max_jobs_); - - // We can't create another job if it would exceed the global total. - if (jobs_.size() + 1 > max_jobs_) - return false; - - // Check whether the pool's constraints are met. - return pool.CanCreateJob(); -} - -// static -HostResolverImpl::JobPoolIndex HostResolverImpl::GetJobPoolIndexForRequest( - const Request* req) { - return POOL_NORMAL; -} - -void HostResolverImpl::ProcessQueuedRequests() { - // Find the highest priority request that can be scheduled. - Request* top_req = NULL; - for (size_t i = 0; i < arraysize(job_pools_); ++i) { - JobPool* pool = job_pools_[i]; - if (pool->HasPendingRequests() && CanCreateJobForPool(*pool)) { - top_req = pool->RemoveTopPendingRequest(); - break; - } - } - - if (!top_req) - return; - - scoped_refptr<Job> job(CreateAndStartJob(top_req)); - - // Search for any other pending request which can piggy-back off this job. - for (size_t pool_i = 0; pool_i < POOL_COUNT; ++pool_i) { - JobPool* pool = job_pools_[pool_i]; - pool->MoveRequestsToJob(job); - } -} - HostResolverImpl::Key HostResolverImpl::GetEffectiveKeyForRequest( const RequestInfo& info) const { HostResolverFlags effective_flags = @@ -1523,58 +1543,22 @@ HostResolverImpl::Key HostResolverImpl::GetEffectiveKeyForRequest( return Key(info.hostname(), effective_address_family, effective_flags); } -HostResolverImpl::Job* HostResolverImpl::CreateAndStartJob(Request* req) { - DCHECK(CanCreateJobForPool(*GetPoolForRequest(req))); - Key key = GetEffectiveKeyForRequest(req->info()); - - req->request_net_log().AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CREATE_JOB, - NULL); - - scoped_refptr<Job> job(new Job(next_job_id_++, this, key, - req->request_net_log(), net_log_)); - job->AddRequest(req); - AddOutstandingJob(job); - job->Start(); - - return job.get(); -} - -int HostResolverImpl::EnqueueRequest(JobPool* pool, Request* req) { - scoped_ptr<Request> req_evicted_from_queue( - pool->InsertPendingRequest(req)); - - // If the queue has become too large, we need to kick something out. - if (req_evicted_from_queue.get()) { - Request* r = req_evicted_from_queue.get(); - int error = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE; - - OnFinishRequest(r->source_net_log(), r->request_net_log(), r->info(), error, - 0 /* os_error (not applicable) */); - - if (r == req) - return error; - - r->OnComplete(error, AddressList()); - } - - return ERR_IO_PENDING; -} - -void HostResolverImpl::CancelAllJobs() { - JobMap jobs; - jobs.swap(jobs_); - for (JobMap::iterator it = jobs.begin(); it != jobs.end(); ++it) - it->second->Cancel(); -} - void HostResolverImpl::AbortAllInProgressJobs() { - for (size_t i = 0; i < arraysize(job_pools_); ++i) - job_pools_[i]->ResetNumOutstandingJobs(); - JobMap jobs; - jobs.swap(jobs_); - for (JobMap::iterator it = jobs.begin(); it != jobs.end(); ++it) { - AbortJob(it->second); - it->second->Cancel(); + base::WeakPtr<HostResolverImpl> self = AsWeakPtr(); + // Scan |jobs_| for running jobs and abort them. + for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ) { + Job* job = it->second; + // Advance the iterator before we might erase it. + ++it; + if (job->is_running()) { + job->Abort(); + // Check if resolver was deleted in a request callback. + if (!self) + return; + } else { + // Keep it in |dispatch_|. + DCHECK(!job->handle().is_null()); + } } } diff --git a/net/base/host_resolver_impl.h b/net/base/host_resolver_impl.h index da69451..d5ff027 100644 --- a/net/base/host_resolver_impl.h +++ b/net/base/host_resolver_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,11 +6,13 @@ #define NET_BASE_HOST_RESOLVER_IMPL_H_ #pragma once +#include <map> #include <vector> #include "base/basictypes.h" #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/threading/non_thread_safe.h" #include "base/time.h" #include "net/base/capturing_net_log.h" @@ -20,15 +22,16 @@ #include "net/base/net_export.h" #include "net/base/net_log.h" #include "net/base/network_change_notifier.h" +#include "net/base/prioritized_dispatcher.h" namespace net { // For each hostname that is requested, HostResolver creates a -// HostResolverImpl::Job. This job gets dispatched to a thread in the global -// WorkerPool, where it runs SystemHostResolverProc(). If requests for that same -// host are made while the job is already outstanding, then they are attached -// to the existing job rather than creating a new one. This avoids doing -// parallel resolves for the same host. +// HostResolverImpl::Job. When this job gets dispatched it creates a ProcJob +// which runs the given HostResolverProc on a WorkerPool thread. If requests for +// that same host are made during the job's lifetime, they are attached to the +// existing job rather than creating a new one. This avoids doing parallel +// resolves for the same host. // // The way these classes fit together is illustrated by: // @@ -41,69 +44,74 @@ namespace net { // Request ... Request Request ... Request Request ... Request // (port1) (port2) (port3) (port4) (port5) (portX) // -// -// When a HostResolverImpl::Job finishes its work in the threadpool, the -// callbacks of each waiting request are run on the origin thread. +// When a HostResolverImpl::Job finishes, the callbacks of each waiting request +// are run on the origin thread. // // Thread safety: This class is not threadsafe, and must only be called // from one thread! // -// The HostResolverImpl enforces |max_jobs_| as the maximum number of concurrent -// threads. +// The HostResolverImpl enforces limits on the maximum number of concurrent +// threads using PrioritizedDispatcher::Limits. // -// Requests are ordered in the queue based on their priority. +// Jobs are ordered in the queue based on their priority and order of arrival. // -// Whenever we try to resolve the host, we post a delayed task to check if host -// resolution (OnLookupComplete) is completed or not. If the original attempt -// hasn't completed, then we start another attempt for host resolution. We take -// the results from the first attempt that finishes and ignore the results from -// all other attempts. - class NET_EXPORT HostResolverImpl : public HostResolver, NON_EXPORTED_BASE(public base::NonThreadSafe), public NetworkChangeNotifier::IPAddressObserver, - public NetworkChangeNotifier::DNSObserver { + public NetworkChangeNotifier::DNSObserver, + public base::SupportsWeakPtr<HostResolverImpl> { public: - // The index into |job_pools_| for the various job pools. Pools with a higher - // index have lower priority. - // - // Note: This is currently unused, since there is a single pool - // for all requests. - enum JobPoolIndex { - POOL_NORMAL = 0, - POOL_COUNT, - }; - - // Creates a HostResolver that first uses the local cache |cache|, and then - // falls back to |resolver_proc|. - // - // If |cache| is NULL, then no caching is used. Otherwise we take - // ownership of the |cache| pointer, and will free it during destructor. + // Parameters for ProcTask which resolves hostnames using HostResolveProc. // // |resolver_proc| is used to perform the actual resolves; it must be // thread-safe since it is run from multiple worker threads. If // |resolver_proc| is NULL then the default host resolver procedure is // used (which is SystemHostResolverProc except if overridden). - // |max_jobs| specifies the maximum number of threads that the host resolver - // will use (not counting potential duplicate attempts). Use - // SetPoolConstraints() to specify finer-grain settings. - // |max_retry_attempts| is the maximum number of times we will retry for host - // resolution. Pass HostResolver::kDefaultRetryAttempts to choose a default - // value. // // For each attempt, we could start another attempt if host is not resolved - // within unresponsive_delay_ time. We keep attempting to resolve the host - // for max_retry_attempts. For every retry attempt, we grow the - // unresponsive_delay_ by the retry_factor_ amount (that is retry interval is - // multiplied by the retry factor each time). Once we have retried - // max_retry_attempts, we give up on additional attempts. + // within |unresponsive_delay| time. We keep attempting to resolve the host + // for |max_retry_attempts|. For every retry attempt, we grow the + // |unresponsive_delay| by the |retry_factor| amount (that is retry interval + // is multiplied by the retry factor each time). Once we have retried + // |max_retry_attempts|, we give up on additional attempts. + // + struct NET_EXPORT_PRIVATE ProcTaskParams { + // Sets up defaults. + ProcTaskParams(HostResolverProc* resolver_proc, size_t max_retry_attempts); + + ~ProcTaskParams(); + + // The procedure to use for resolving host names. This will be NULL, except + // in the case of unit-tests which inject custom host resolving behaviors. + scoped_refptr<HostResolverProc> resolver_proc; + + // Maximum number retry attempts to resolve the hostname. + // Pass HostResolver::kDefaultRetryAttempts to choose a default value. + size_t max_retry_attempts; + + // This is the limit after which we make another attempt to resolve the host + // if the worker thread has not responded yet. + base::TimeDelta unresponsive_delay; + + // Factor to grow |unresponsive_delay| when we re-re-try. + uint32 retry_factor; + }; + + // Creates a HostResolver that first uses the local cache |cache|, and then + // falls back to |proc_params.resolver_proc|. + // + // If |cache| is NULL, then no caching is used. Otherwise we take + // ownership of the |cache| pointer, and will free it during destruction. + // + // |job_limits| specifies the maximum number of jobs that the resolver will + // run at once (not counting potential duplicate attempts). // // |net_log| must remain valid for the life of the HostResolverImpl. - HostResolverImpl(HostResolverProc* resolver_proc, - HostCache* cache, - size_t max_jobs, - size_t max_retry_attempts, + // TODO(szym): change to scoped_ptr<HostCache>. + HostResolverImpl(HostCache* cache, + const PrioritizedDispatcher::Limits& job_limits, + const ProcTaskParams& proc_params, NetLog* net_log); // If any completion callbacks are pending when the resolver is destroyed, @@ -111,20 +119,9 @@ class NET_EXPORT HostResolverImpl // be called. virtual ~HostResolverImpl(); - // Applies a set of constraints for requests that belong to the specified - // pool. NOTE: Don't call this after requests have been already been started. - // - // |pool_index| -- Specifies which pool these constraints should be applied - // to. - // |max_outstanding_jobs| -- How many concurrent jobs are allowed for this - // pool. - // |max_pending_requests| -- How many requests can be enqueued for this pool - // before we start dropping requests. Dropped - // requests fail with - // ERR_HOST_RESOLVER_QUEUE_TOO_LARGE. - void SetPoolConstraints(JobPoolIndex pool_index, - size_t max_outstanding_jobs, - size_t max_pending_requests); + // Configures maximum number of Jobs in the queue. Exposed for testing. + // Only allowed when the queue is empty. + void SetMaxQueuedJobs(size_t value); // HostResolver methods: virtual int Resolve(const RequestInfo& info, @@ -142,19 +139,13 @@ class NET_EXPORT HostResolverImpl virtual HostCache* GetHostCache() OVERRIDE; private: - // Allow tests to access our innards for testing purposes. - friend class LookupAttemptHostResolverProc; - - // Allow tests to access our innards for testing purposes. - FRIEND_TEST_ALL_PREFIXES(HostResolverImplTest, MultipleAttempts); - class Job; - class JobPool; + class ProcTask; class IPv6ProbeJob; class Request; - typedef std::vector<Request*> RequestsList; typedef HostCache::Key Key; - typedef std::map<Key, scoped_refptr<Job> > JobMap; + typedef std::map<Key, Job*> JobMap; + typedef std::vector<Request*> RequestsList; // Helper used by |Resolve()| and |ResolveFromCache()|. Performs IP // literal and cache lookup, returns OK if successful, @@ -181,153 +172,48 @@ class NET_EXPORT HostResolverImpl int* net_error, AddressList* addresses); - // Returns the HostResolverProc to use for this instance. - HostResolverProc* effective_resolver_proc() const { - return resolver_proc_ ? - resolver_proc_.get() : HostResolverProc::GetDefault(); - } - - // Adds a job to outstanding jobs list. - void AddOutstandingJob(Job* job); - - // Returns the outstanding job for |key|, or NULL if there is none. - Job* FindOutstandingJob(const Key& key); - - // Removes |job| from the outstanding jobs list. - void RemoveOutstandingJob(Job* job); - - // Callback for when |job| has completed with |net_error| and |addrlist|. - void OnJobComplete(Job* job, int net_error, int os_error, - const AddressList& addrlist); - - // Aborts |job|. Same as OnJobComplete() except does not remove |job| - // from |jobs_| and does not cache the result (ERR_ABORTED). - void AbortJob(Job* job); - - // Used by both OnJobComplete() and AbortJob(); - void OnJobCompleteInternal(Job* job, int net_error, int os_error, - const AddressList& addrlist); - - // Called when a request has just been started. - void OnStartRequest(const BoundNetLog& source_net_log, - const BoundNetLog& request_net_log, - const RequestInfo& info); - - // Called when a request has just completed (before its callback is run). - void OnFinishRequest(const BoundNetLog& source_net_log, - const BoundNetLog& request_net_log, - const RequestInfo& info, - int net_error, - int os_error); - - // Called when a request has been cancelled. - void OnCancelRequest(const BoundNetLog& source_net_log, - const BoundNetLog& request_net_log, - const RequestInfo& info); - - // Notify IPv6ProbeJob not to call back, and discard reference to the job. + // Notifies IPv6ProbeJob not to call back, and discard reference to the job. void DiscardIPv6ProbeJob(); // Callback from IPv6 probe activity. void IPv6ProbeSetDefaultAddressFamily(AddressFamily address_family); - // Returns true if the constraints for |pool| are met, and a new job can be - // created for this pool. - bool CanCreateJobForPool(const JobPool& pool) const; - - // Returns the index of the pool that request |req| maps to. - static JobPoolIndex GetJobPoolIndexForRequest(const Request* req); - - JobPool* GetPoolForRequest(const Request* req) { - return job_pools_[GetJobPoolIndexForRequest(req)]; - } - - // Starts up to 1 job given the current pool constraints. This job - // may have multiple requests attached to it. - void ProcessQueuedRequests(); - // Returns the (hostname, address_family) key to use for |info|, choosing an // "effective" address family by inheriting the resolver's default address // family when the request leaves it unspecified. Key GetEffectiveKeyForRequest(const RequestInfo& info) const; - // Attaches |req| to a new job, and starts it. Returns that job. - Job* CreateAndStartJob(Request* req); - - // Adds a pending request |req| to |pool|. - int EnqueueRequest(JobPool* pool, Request* req); + // Called by |job| when it has finished running. Records the result in cache + // if necessary and dispatches another job if possible. + void OnJobFinished(Job* job, const AddressList& addrlist); - // Cancels all jobs. - void CancelAllJobs(); + // Removes |job| from |jobs_|. + void RemoveJob(Job* job); - // Aborts all in progress jobs (but might start new ones). + // Aborts all in progress jobs and notifies their requests. + // Might start new jobs. void AbortAllInProgressJobs(); // NetworkChangeNotifier::IPAddressObserver methods: virtual void OnIPAddressChanged() OVERRIDE; - // Helper methods to get and set max_retry_attempts_. - size_t max_retry_attempts() const { - return max_retry_attempts_; - } - void set_max_retry_attempts(const size_t max_retry_attempts) { - max_retry_attempts_ = max_retry_attempts; - } - - // Helper methods for unit tests to get and set unresponsive_delay_. - base::TimeDelta unresponsive_delay() const { return unresponsive_delay_; } - void set_unresponsive_delay(const base::TimeDelta& unresponsive_delay) { - unresponsive_delay_ = unresponsive_delay; - } - - // Helper methods to get and set retry_factor_. - uint32 retry_factor() const { - return retry_factor_; - } - void set_retry_factor(const uint32 retry_factor) { - retry_factor_ = retry_factor; - } - // NetworkChangeNotifier::OnDNSChanged methods: virtual void OnDNSChanged() OVERRIDE; // Cache of host resolution results. scoped_ptr<HostCache> cache_; - // Map from hostname to outstanding job. + // Map from HostCache::Key to a Job. JobMap jobs_; - // Maximum number of concurrent jobs allowed, across all pools. Each job may - // create multiple concurrent resolve attempts for the hostname. - size_t max_jobs_; - - // Maximum number retry attempts to resolve the hostname. - size_t max_retry_attempts_; - - // This is the limit after which we make another attempt to resolve the host - // if the worker thread has not responded yet. Allow unit tests to change the - // value. - base::TimeDelta unresponsive_delay_; - - // Factor to grow unresponsive_delay_ when we re-re-try. Allow unit tests to - // change the value. - uint32 retry_factor_; - - // The information to track pending requests for a JobPool, as well as - // how many outstanding jobs the pool already has, and its constraints. - JobPool* job_pools_[POOL_COUNT]; - - // The job that OnJobComplete() is currently processing (needed in case - // HostResolver gets deleted from within the callback). - scoped_refptr<Job> cur_completing_job_; + // Starts Jobs according to their priority and the configured limits. + PrioritizedDispatcher dispatcher_; - // Monotonically increasing ID number to assign to the next job. - // The only consumer of this ID is the requests tracing code. - int next_job_id_; + // Limit on the maximum number of jobs queued in |dispatcher_|. + size_t max_queued_jobs_; - // The procedure to use for resolving host names. This will be NULL, except - // in the case of unit-tests which inject custom host resolving behaviors. - scoped_refptr<HostResolverProc> resolver_proc_; + // Parameters for ProcTask. + ProcTaskParams proc_params_; // Address family to use when the request doesn't specify one. AddressFamily default_address_family_; diff --git a/net/base/host_resolver_impl_unittest.cc b/net/base/host_resolver_impl_unittest.cc index 7acf244..9a299c1 100644 --- a/net/base/host_resolver_impl_unittest.cc +++ b/net/base/host_resolver_impl_unittest.cc @@ -27,10 +27,6 @@ #include "net/base/test_completion_callback.h" #include "testing/gtest/include/gtest/gtest.h" -// TODO(eroman): -// - Test mixing async with sync (in particular how does sync update the -// cache while an async is already pending). - namespace net { using base::TimeDelta; @@ -39,9 +35,37 @@ using base::TimeTicks; static const size_t kMaxJobs = 10u; static const size_t kMaxRetryAttempts = 4u; +PrioritizedDispatcher::Limits DefaultLimits() { + PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, kMaxJobs); + return limits; +} + +HostResolverImpl::ProcTaskParams DefaultParams( + HostResolverProc* resolver_proc) { + return HostResolverImpl::ProcTaskParams(resolver_proc, + kMaxRetryAttempts); +} + HostResolverImpl* CreateHostResolverImpl(HostResolverProc* resolver_proc) { - return new HostResolverImpl(resolver_proc, HostCache::CreateDefaultCache(), - kMaxJobs, kMaxRetryAttempts, NULL); + return new HostResolverImpl( + HostCache::CreateDefaultCache(), + DefaultLimits(), + DefaultParams(resolver_proc), + NULL); +} + +// This HostResolverImpl will only allow 1 outstanding resolve at a time. +HostResolverImpl* CreateSerialHostResolverImpl( + HostResolverProc* resolver_proc) { + HostResolverImpl::ProcTaskParams params = DefaultParams(resolver_proc); + params.max_retry_attempts = 0u; + + PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1); + + return new HostResolverImpl(HostCache::CreateDefaultCache(), + limits, + params, + NULL); } // Helper to create a HostResolver::RequestInfo. @@ -482,18 +506,18 @@ class WaitingHostResolverProc : public HostResolverProc { base::WaitableEvent is_signaled_; }; -TEST_F(HostResolverImplTest, CanceledAsynchronousLookup) { +TEST_F(HostResolverImplTest, AbortedAsynchronousLookup) { scoped_refptr<WaitingHostResolverProc> resolver_proc( new WaitingHostResolverProc(NULL)); CapturingNetLog net_log(CapturingNetLog::kUnbounded); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); { + // This resolver will be destroyed while a lookup is running on WorkerPool. scoped_ptr<HostResolver> host_resolver( - new HostResolverImpl(resolver_proc, - HostCache::CreateDefaultCache(), - kMaxJobs, - kMaxRetryAttempts, + new HostResolverImpl(HostCache::CreateDefaultCache(), + DefaultLimits(), + DefaultParams(resolver_proc), &net_log)); AddressList addrlist; const int kPortnum = 80; @@ -526,12 +550,15 @@ TEST_F(HostResolverImplTest, CanceledAsynchronousLookup) { pos = ExpectLogContainsSomewhereAfter(net_log_entries, pos + 1, NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, NetLog::PHASE_BEGIN); - // Both Job and Request need to be cancelled. + pos = ExpectLogContainsSomewhereAfter(net_log_entries, pos + 1, + NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK, + NetLog::PHASE_BEGIN); + // Both Request and ProcTask need to be cancelled. (The Job is "aborted".) pos = ExpectLogContainsSomewhereAfter(net_log_entries, pos + 1, NetLog::TYPE_CANCELLED, NetLog::PHASE_NONE); - // Don't care about order in which they end, or when the other one is - // cancelled. + // Don't care about order in which Request, Job and ProcTask end, or when the + // other one is cancelled. ExpectLogContainsSomewhereAfter(net_log_entries, pos + 1, NetLog::TYPE_CANCELLED, NetLog::PHASE_NONE); @@ -539,6 +566,9 @@ TEST_F(HostResolverImplTest, CanceledAsynchronousLookup) { NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, NetLog::PHASE_END); ExpectLogContainsSomewhereAfter(net_log_entries, pos + 1, + NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK, + NetLog::PHASE_END); + ExpectLogContainsSomewhereAfter(net_log_entries, pos + 1, NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, NetLog::PHASE_END); @@ -942,9 +972,8 @@ TEST_F(HostResolverImplTest, StartWithinCallback) { new CapturingHostResolverProc(NULL)); // Turn off caching for this host resolver. - scoped_ptr<HostResolver> host_resolver( - new HostResolverImpl(resolver_proc, NULL, kMaxJobs, kMaxRetryAttempts, - NULL)); + scoped_ptr<HostResolver> host_resolver(new HostResolverImpl( + NULL, DefaultLimits(), DefaultParams(resolver_proc), NULL)); // The class will receive callbacks for when each resolve completes. It // checks that the right things happened. @@ -1024,12 +1053,11 @@ TEST_F(HostResolverImplTest, BypassCache) { // Test that IP address changes flush the cache. TEST_F(HostResolverImplTest, FlushCacheOnIPAddressChange) { scoped_ptr<HostResolver> host_resolver( - new HostResolverImpl(NULL, HostCache::CreateDefaultCache(), kMaxJobs, - kMaxRetryAttempts, NULL)); + CreateHostResolverImpl(NULL)); AddressList addrlist; - // Resolve "host1". + // Resolve "host1". Assume that ScopedDefaultHostResolverProc resolves all. HostResolver::RequestInfo info1(HostPortPair("host1", 70)); TestCompletionCallback callback; int rv = host_resolver->Resolve(info1, &addrlist, callback.callback(), NULL, @@ -1083,15 +1111,9 @@ TEST_F(HostResolverImplTest, AbortOnIPAddressChanged) { TEST_F(HostResolverImplTest, ObeyPoolConstraintsAfterIPAddressChange) { scoped_refptr<WaitingHostResolverProc> resolver_proc( new WaitingHostResolverProc(CreateCatchAllHostResolverProc())); - scoped_ptr<HostResolverImpl> host_resolver( - new HostResolverImpl(resolver_proc, HostCache::CreateDefaultCache(), - kMaxJobs, kMaxRetryAttempts, NULL)); - const size_t kMaxOutstandingJobs = 1u; - const size_t kMaxPendingRequests = 1000000u; // not relevant. - host_resolver->SetPoolConstraints(HostResolverImpl::POOL_NORMAL, - kMaxOutstandingJobs, - kMaxPendingRequests); + scoped_ptr<HostResolverImpl> host_resolver( + CreateSerialHostResolverImpl(resolver_proc)); // Resolve "host1". HostResolver::RequestInfo info(HostPortPair("host1", 70)); @@ -1179,12 +1201,8 @@ TEST_F(HostResolverImplTest, HigherPriorityRequestsStartedFirst) { scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(NULL)); - // This HostResolverImpl will only allow 1 outstanding resolve at a time. - size_t kMaxJobs = 1u; - const size_t kRetryAttempts = 0u; - scoped_ptr<HostResolver> host_resolver( - new HostResolverImpl(resolver_proc, HostCache::CreateDefaultCache(), - kMaxJobs, kRetryAttempts, NULL)); + scoped_ptr<HostResolverImpl> host_resolver( + CreateSerialHostResolverImpl(resolver_proc)); // Note that at this point the CapturingHostResolverProc is blocked, so any // requests we make will not complete. @@ -1241,12 +1259,8 @@ TEST_F(HostResolverImplTest, CancelPendingRequest) { scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(NULL)); - // This HostResolverImpl will only allow 1 outstanding resolve at a time. - const size_t kMaxJobs = 1u; - const size_t kRetryAttempts = 0u; - scoped_ptr<HostResolver> host_resolver( - new HostResolverImpl(resolver_proc, HostCache::CreateDefaultCache(), - kMaxJobs, kRetryAttempts, NULL)); + scoped_ptr<HostResolverImpl> host_resolver( + CreateSerialHostResolverImpl(resolver_proc)); // Note that at this point the CapturingHostResolverProc is blocked, so any // requests we make will not complete. @@ -1256,8 +1270,8 @@ TEST_F(HostResolverImplTest, CancelPendingRequest) { CreateResolverRequest("req1", HIGHEST), // Will cancel. CreateResolverRequest("req2", MEDIUM), CreateResolverRequest("req3", LOW), - CreateResolverRequest("req4", HIGHEST), // Will cancel. - CreateResolverRequest("req5", LOWEST), // Will cancel. + CreateResolverRequest("req4", HIGHEST), // Will cancel. + CreateResolverRequest("req5", LOWEST), // Will cancel. CreateResolverRequest("req6", MEDIUM), }; @@ -1306,18 +1320,12 @@ TEST_F(HostResolverImplTest, QueueOverflow) { scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(NULL)); - // This HostResolverImpl will only allow 1 outstanding resolve at a time. - const size_t kMaxOutstandingJobs = 1u; - const size_t kRetryAttempts = 0u; - scoped_ptr<HostResolverImpl> host_resolver(new HostResolverImpl( - resolver_proc, HostCache::CreateDefaultCache(), kMaxOutstandingJobs, - kRetryAttempts, NULL)); - - // Only allow up to 3 requests to be enqueued at a time. - const size_t kMaxPendingRequests = 3u; - host_resolver->SetPoolConstraints(HostResolverImpl::POOL_NORMAL, - kMaxOutstandingJobs, - kMaxPendingRequests); + scoped_ptr<HostResolverImpl> host_resolver( + CreateSerialHostResolverImpl(resolver_proc)); + + // Allow only 3 queued jobs. + const size_t kMaxPendingJobs = 3u; + host_resolver->SetMaxQueuedJobs(kMaxPendingJobs); // Note that at this point the CapturingHostResolverProc is blocked, so any // requests we make will not complete. @@ -1388,11 +1396,8 @@ TEST_F(HostResolverImplTest, SetDefaultAddressFamily_IPv4) { new CapturingHostResolverProc(new EchoingHostResolverProc)); // This HostResolverImpl will only allow 1 outstanding resolve at a time. - const size_t kMaxOutstandingJobs = 1u; - const size_t kRetryAttempts = 0u; - scoped_ptr<HostResolverImpl> host_resolver(new HostResolverImpl( - resolver_proc, HostCache::CreateDefaultCache(), kMaxOutstandingJobs, - kRetryAttempts, NULL)); + scoped_ptr<HostResolverImpl> host_resolver( + CreateSerialHostResolverImpl(resolver_proc)); host_resolver->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4); @@ -1458,12 +1463,8 @@ TEST_F(HostResolverImplTest, SetDefaultAddressFamily_IPv6) { scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(new EchoingHostResolverProc)); - // This HostResolverImpl will only allow 1 outstanding resolve at a time. - const size_t kMaxOutstandingJobs = 1u; - const size_t kRetryAttempts = 0u; - scoped_ptr<HostResolverImpl> host_resolver(new HostResolverImpl( - resolver_proc, HostCache::CreateDefaultCache(), kMaxOutstandingJobs, - kRetryAttempts, NULL)); + scoped_ptr<HostResolverImpl> host_resolver( + CreateSerialHostResolverImpl(resolver_proc)); host_resolver->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV6); @@ -1573,16 +1574,19 @@ TEST_F(HostResolverImplTest, MultipleAttempts) { scoped_refptr<LookupAttemptHostResolverProc> resolver_proc( new LookupAttemptHostResolverProc( NULL, kAttemptNumberToResolve, kTotalAttempts)); - HostCache* cache = HostCache::CreateDefaultCache(); - scoped_ptr<HostResolverImpl> host_resolver( - new HostResolverImpl(resolver_proc, cache, kMaxJobs, kMaxRetryAttempts, - NULL)); + + HostResolverImpl::ProcTaskParams params = DefaultParams(resolver_proc); // Specify smaller interval for unresponsive_delay_ for HostResolverImpl so // that unit test runs faster. For example, this test finishes in 1.5 secs // (500ms * 3). - TimeDelta kUnresponsiveTime = TimeDelta::FromMilliseconds(500); - host_resolver->set_unresponsive_delay(kUnresponsiveTime); + params.unresponsive_delay = TimeDelta::FromMilliseconds(500); + + scoped_ptr<HostResolverImpl> host_resolver( + new HostResolverImpl(HostCache::CreateDefaultCache(), + DefaultLimits(), + params, + NULL)); // Resolve "host1". HostResolver::RequestInfo info(HostPortPair("host1", 70)); diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h index 44f6f22..9a7e208 100644 --- a/net/base/net_log_event_type_list.h +++ b/net/base/net_log_event_type_list.h @@ -58,32 +58,46 @@ EVENT_TYPE(HOST_RESOLVER_IMPL) // "net_error": <The net error code integer for the failure>, // "os_error": <The exact error code integer that getaddrinfo() returned>, // } - EVENT_TYPE(HOST_RESOLVER_IMPL_REQUEST) // This event is logged when a request is handled by a cache entry. EVENT_TYPE(HOST_RESOLVER_IMPL_CACHE_HIT) -// This event means a request was queued/dequeued for subsequent job creation, -// because there are already too many active HostResolverImpl::Jobs. +// This event is created when a new HostResolverImpl::Job is about to be created +// for a request. +EVENT_TYPE(HOST_RESOLVER_IMPL_CREATE_JOB) + +// The creation/completion of a HostResolverImpl::Job which is created for +// Requests that cannot be resolved synchronously. // // The BEGIN phase contains the following parameters: // // { -// "priority": <Priority of the queued request>, +// "host": <Hostname associated with the request>, +// "source_dependency": <Source id, if any, of what created the request>, +// } +// +// On success, the END phase has these parameters: +// { +// "address_list": <The host name being resolved>, +// } +// If an error occurred, the END phase will contain these parameters: +// { +// "net_error": <The net error code integer for the failure>, +// "os_error": <The exact error code integer that getaddrinfo() returned>, // } -EVENT_TYPE(HOST_RESOLVER_IMPL_JOB_POOL_QUEUE) +EVENT_TYPE(HOST_RESOLVER_IMPL_JOB) -// This event is created when a new HostResolverImpl::Request is evicted from -// JobPool without a Job being created, because the limit on number of queued -// Requests was reached. -EVENT_TYPE(HOST_RESOLVER_IMPL_JOB_POOL_QUEUE_EVICTED) +// This event is created when a HostResolverImpl::Job is evicted from +// PriorityDispatch before it can start, because the limit on number of queued +// Jobs was reached. +EVENT_TYPE(HOST_RESOLVER_IMPL_JOB_EVICTED) -// This event is created when a new HostResolverImpl::Job is about to be created -// for a request. -EVENT_TYPE(HOST_RESOLVER_IMPL_CREATE_JOB) +// This event is created when a HostResolverImpl::Job is started by +// PriorityDispatch. +EVENT_TYPE(HOST_RESOLVER_IMPL_JOB_STARTED) -// This event is created when HostResolverImpl::Job is about to start a new +// This event is created when HostResolverImpl::ProcJob is about to start a new // attempt to resolve the host. // // The ATTEMPT_STARTED event has the parameters: @@ -93,8 +107,8 @@ EVENT_TYPE(HOST_RESOLVER_IMPL_CREATE_JOB) // } EVENT_TYPE(HOST_RESOLVER_IMPL_ATTEMPT_STARTED) -// This event is created when HostResolverImpl::Job has finished resolving the -// host. +// This event is created when HostResolverImpl::ProcJob has finished resolving +// the host. // // The ATTEMPT_FINISHED event has the parameters: // @@ -109,24 +123,52 @@ EVENT_TYPE(HOST_RESOLVER_IMPL_ATTEMPT_STARTED) EVENT_TYPE(HOST_RESOLVER_IMPL_ATTEMPT_FINISHED) // This is logged for a request when it's attached to a -// HostResolverImpl::Job. When this occurs without a preceding +// HostResolverImpl::Job. When this occurs without a preceding // HOST_RESOLVER_IMPL_CREATE_JOB entry, it means the request was attached to an // existing HostResolverImpl::Job. // -// If the BoundNetLog used to create the event has a valid source id, the BEGIN -// phase contains the following parameters: +// The event contains the following parameters: // // { // "source_dependency": <Source identifier for the attached Job>, // } +// EVENT_TYPE(HOST_RESOLVER_IMPL_JOB_ATTACH) -// The creation/completion of a host resolve (DNS) job. +// This event is logged for the job to which the request is attached. +// In that case, the event contains the following parameters: +// +// { +// "source_dependency": <Source identifier for the attached Request>, +// "priority": <New priority of the job>, +// } +EVENT_TYPE(HOST_RESOLVER_IMPL_JOB_REQUEST_ATTACH) + +// This is logged for a job when a request is cancelled and detached. +// +// The event contains the following parameters: +// +// { +// "source_dependency": <Source identifier for the detached Request>, +// "priority": <New priority of the job>, +// } +EVENT_TYPE(HOST_RESOLVER_IMPL_JOB_REQUEST_DETACH) + +// Logged for a HostResolverImpl::Job when it creates a ProcTask. +// +// The event contains the following parameters: +// +// { +// "source_dependency": <Source id of parent HostResolverImpl::Job>, +// } +EVENT_TYPE(HOST_RESOLVER_IMPL_CREATE_PROC_TASK) + +// The creation/completion of a HostResolverImpl::ProcTask to call getaddrinfo. // The BEGIN phase contains the following parameters: // // { -// "host": <Hostname associated with the request>, -// "source_dependency": <Source id, if any, of what created the request>, +// "hostname": <Hostname associated with the request>, +// "source_dependency": <Source id of parent HostResolverImpl::Job>, // } // // On success, the END phase has these parameters: @@ -138,7 +180,7 @@ EVENT_TYPE(HOST_RESOLVER_IMPL_JOB_ATTACH) // "net_error": <The net error code integer for the failure>, // "os_error": <The exact error code integer that getaddrinfo() returned>, // } -EVENT_TYPE(HOST_RESOLVER_IMPL_JOB) +EVENT_TYPE(HOST_RESOLVER_IMPL_PROC_TASK) // ------------------------------------------------------------------------ // InitProxyResolver diff --git a/net/base/net_log_source_type_list.h b/net/base/net_log_source_type_list.h index a8565d9..d4aac9f 100644 --- a/net/base/net_log_source_type_list.h +++ b/net/base/net_log_source_type_list.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,14 +16,16 @@ SOURCE_TYPE(SOCKET, 5) SOURCE_TYPE(SPDY_SESSION, 6) SOURCE_TYPE(HOST_RESOLVER_IMPL_REQUEST, 7) SOURCE_TYPE(HOST_RESOLVER_IMPL_JOB, 8) -SOURCE_TYPE(DISK_CACHE_ENTRY, 9) -SOURCE_TYPE(MEMORY_CACHE_ENTRY, 10) -SOURCE_TYPE(HTTP_STREAM_JOB, 11) -SOURCE_TYPE(EXPONENTIAL_BACKOFF_THROTTLING, 12) -SOURCE_TYPE(DNS_TRANSACTION, 13) -SOURCE_TYPE(ASYNC_HOST_RESOLVER_REQUEST, 14) -SOURCE_TYPE(UDP_SOCKET, 15) -SOURCE_TYPE(CERT_VERIFIER_JOB, 16) -SOURCE_TYPE(HTTP_PIPELINED_CONNECTION, 17) +SOURCE_TYPE(HOST_RESOLVER_IMPL_PROC_TASK, 9) +SOURCE_TYPE(DISK_CACHE_ENTRY, 10) +SOURCE_TYPE(MEMORY_CACHE_ENTRY, 11) +SOURCE_TYPE(HTTP_STREAM_JOB, 12) +SOURCE_TYPE(EXPONENTIAL_BACKOFF_THROTTLING, 13) +SOURCE_TYPE(DNS_TRANSACTION, 14) +SOURCE_TYPE(ASYNC_HOST_RESOLVER_REQUEST, 15) +SOURCE_TYPE(UDP_SOCKET, 16) +SOURCE_TYPE(CERT_VERIFIER_JOB, 17) +SOURCE_TYPE(HTTP_PIPELINED_CONNECTION, 18) + +SOURCE_TYPE(COUNT, 19) // Always keep this as the last entry. -SOURCE_TYPE(COUNT, 18) // Always keep this as the last entry. |