diff options
-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. |