summaryrefslogtreecommitdiffstats
path: root/net/proxy
diff options
context:
space:
mode:
authoreroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-08 21:10:27 +0000
committereroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-08 21:10:27 +0000
commit0007f1434eafa4e955ff916d50a7a39fd850d435 (patch)
tree278dca04911fbefee519ac7ab449d208f4938a3d /net/proxy
parent41c9921ad0ffa548263262477777e865cd85b144 (diff)
downloadchromium_src-0007f1434eafa4e955ff916d50a7a39fd850d435.zip
chromium_src-0007f1434eafa4e955ff916d50a7a39fd850d435.tar.gz
chromium_src-0007f1434eafa4e955ff916d50a7a39fd850d435.tar.bz2
Revert 51877, since SpdyNetworkTransactionTest.CorruptFrameSessionError started failing after this check-in (but only on vista modules builder).
BUG=48588 Original CL description: Add the capability to run multiple proxy PAC scripts in parallel. Refactors SingleThreadedProxyResolver into MultiThreadedProxyResolver. New threads are created lazily on demand, up to a fixed maximum. Note that this CL does NOT change the policy in Chrome -- it will continue to use a single thread for proxy resolving, but using the new code to do so. BUG=11079 Review URL: http://codereview.chromium.org/2822043 TBR=eroman@chromium.org Review URL: http://codereview.chromium.org/2945004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51893 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/proxy')
-rw-r--r--net/proxy/multi_threaded_proxy_resolver.cc588
-rw-r--r--net/proxy/multi_threaded_proxy_resolver.h144
-rw-r--r--net/proxy/proxy_resolver.h4
-rw-r--r--net/proxy/proxy_resolver_js_bindings.cc6
-rw-r--r--net/proxy/proxy_resolver_js_bindings.h3
-rw-r--r--net/proxy/proxy_resolver_v8.cc4
-rw-r--r--net/proxy/proxy_resolver_v8.h1
-rw-r--r--net/proxy/proxy_resolver_v8_unittest.cc2
-rw-r--r--net/proxy/proxy_service.cc96
-rw-r--r--net/proxy/proxy_service.h4
-rw-r--r--net/proxy/single_threaded_proxy_resolver.cc353
-rw-r--r--net/proxy/single_threaded_proxy_resolver.h94
-rw-r--r--net/proxy/single_threaded_proxy_resolver_unittest.cc (renamed from net/proxy/multi_threaded_proxy_resolver_unittest.cc)388
-rw-r--r--net/proxy/sync_host_resolver_bridge.cc15
-rw-r--r--net/proxy/sync_host_resolver_bridge.h19
-rw-r--r--net/proxy/sync_host_resolver_bridge_unittest.cc41
16 files changed, 588 insertions, 1174 deletions
diff --git a/net/proxy/multi_threaded_proxy_resolver.cc b/net/proxy/multi_threaded_proxy_resolver.cc
deleted file mode 100644
index a55fef9..0000000
--- a/net/proxy/multi_threaded_proxy_resolver.cc
+++ /dev/null
@@ -1,588 +0,0 @@
-// Copyright (c) 2010 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.
-
-#include "net/proxy/multi_threaded_proxy_resolver.h"
-
-#include "base/message_loop.h"
-#include "base/string_util.h"
-#include "base/thread.h"
-#include "net/base/capturing_net_log.h"
-#include "net/base/net_errors.h"
-#include "net/proxy/proxy_info.h"
-
-// TODO(eroman): Have the MultiThreadedProxyResolver clear its PAC script
-// data when SetPacScript fails. That will reclaim memory when
-// testing bogus scripts.
-
-namespace net {
-
-namespace {
-
-class PurgeMemoryTask : public base::RefCountedThreadSafe<PurgeMemoryTask> {
- public:
- explicit PurgeMemoryTask(ProxyResolver* resolver) : resolver_(resolver) {}
- void PurgeMemory() { resolver_->PurgeMemory(); }
- private:
- friend class base::RefCountedThreadSafe<PurgeMemoryTask>;
- ~PurgeMemoryTask() {}
- ProxyResolver* resolver_;
-};
-
-} // namespace
-
-// An "executor" is a job-runner for PAC requests. It encapsulates a worker
-// thread and a synchronous ProxyResolver (which will be operated on said
-// thread.)
-class MultiThreadedProxyResolver::Executor
- : public base::RefCountedThreadSafe<MultiThreadedProxyResolver::Executor > {
- public:
- // |coordinator| must remain valid throughout our lifetime. It is used to
- // signal when the executor is ready to receive work by calling
- // |coordinator->OnExecutorReady()|.
- // The constructor takes ownership of |resolver|.
- // |thread_number| is an identifier used when naming the worker thread.
- Executor(MultiThreadedProxyResolver* coordinator,
- ProxyResolver* resolver,
- int thread_number);
-
- // Submit a job to this executor.
- void StartJob(Job* job);
-
- // Callback for when a job has completed running on the executor's thread.
- void OnJobCompleted(Job* job);
-
- // Cleanup the executor. Cancels all outstanding work, and frees the thread
- // and resolver.
- void Destroy();
-
- void PurgeMemory();
-
- // Returns the outstanding job, or NULL.
- Job* outstanding_job() const { return outstanding_job_.get(); }
-
- ProxyResolver* resolver() { return resolver_.get(); }
-
- int thread_number() const { return thread_number_; }
-
- private:
- friend class base::RefCountedThreadSafe<Executor>;
- ~Executor();
-
- MultiThreadedProxyResolver* coordinator_;
- const int thread_number_;
-
- // The currently active job for this executor (either a SetPacScript or
- // GetProxyForURL task).
- scoped_refptr<Job> outstanding_job_;
-
- // The synchronous resolver implementation.
- scoped_ptr<ProxyResolver> resolver_;
-
- // The thread where |resolver_| is run on.
- // Note that declaration ordering is important here. |thread_| needs to be
- // destroyed *before* |resolver_|, in case |resolver_| is currently
- // executing on |thread_|.
- scoped_ptr<base::Thread> thread_;
-};
-
-// MultiThreadedProxyResolver::Job ---------------------------------------------
-
-class MultiThreadedProxyResolver::Job
- : public base::RefCountedThreadSafe<MultiThreadedProxyResolver::Job> {
- public:
- // Identifies the subclass of Job (only being used for debugging purposes).
- enum Type {
- TYPE_GET_PROXY_FOR_URL,
- TYPE_SET_PAC_SCRIPT,
- TYPE_SET_PAC_SCRIPT_INTERNAL,
- };
-
- Job(Type type, CompletionCallback* user_callback)
- : type_(type),
- user_callback_(user_callback),
- executor_(NULL),
- was_cancelled_(false) {
- }
-
- void set_executor(Executor* executor) {
- executor_ = executor;
- }
-
- // The "executor" is the job runner that is scheduling this job. If
- // this job has not been submitted to an executor yet, this will be
- // NULL (and we know it hasn't started yet).
- Executor* executor() {
- return executor_;
- }
-
- // Mark the job as having been cancelled.
- void Cancel() {
- was_cancelled_ = true;
- }
-
- // Returns true if Cancel() has been called.
- bool was_cancelled() const { return was_cancelled_; }
-
- Type type() const { return type_; }
-
- // Returns true if this job still has a user callback. Some jobs
- // do not have a user callback, because they were helper jobs
- // scheduled internally (for example TYPE_SET_PAC_SCRIPT_INTERNAL).
- //
- // Otherwise jobs that correspond with user-initiated work will
- // have a non-NULL callback up until the callback is run.
- bool has_user_callback() const { return user_callback_ != NULL; }
-
- // This method is called when the job is inserted into a wait queue
- // because no executors were ready to accept it.
- virtual void WaitingForThread() {}
-
- // This method is called just before the job is posted to the work thread.
- virtual void FinishedWaitingForThread() {}
-
- // This method is called on the worker thread to do the job's work. On
- // completion, implementors are expected to call OnJobCompleted() on
- // |origin_loop|.
- virtual void Run(MessageLoop* origin_loop) = 0;
-
- protected:
- void OnJobCompleted() {
- // |executor_| will be NULL if the executor has already been deleted.
- if (executor_)
- executor_->OnJobCompleted(this);
- }
-
- void RunUserCallback(int result) {
- DCHECK(has_user_callback());
- CompletionCallback* callback = user_callback_;
- // Null the callback so has_user_callback() will now return false.
- user_callback_ = NULL;
- callback->Run(result);
- }
-
- friend class base::RefCountedThreadSafe<MultiThreadedProxyResolver::Job>;
-
- virtual ~Job() {}
-
- private:
- const Type type_;
- CompletionCallback* user_callback_;
- Executor* executor_;
- bool was_cancelled_;
-};
-
-// MultiThreadedProxyResolver::SetPacScriptJob ---------------------------------
-
-// Runs on the worker thread to call ProxyResolver::SetPacScript.
-class MultiThreadedProxyResolver::SetPacScriptJob
- : public MultiThreadedProxyResolver::Job {
- public:
- SetPacScriptJob(const GURL& pac_url,
- const string16& pac_script,
- CompletionCallback* callback)
- : Job(callback ? TYPE_SET_PAC_SCRIPT : TYPE_SET_PAC_SCRIPT_INTERNAL,
- callback),
- pac_url_(pac_url),
- pac_script_(pac_script) {
- }
-
- // Runs on the worker thread.
- virtual void Run(MessageLoop* origin_loop) {
- ProxyResolver* resolver = executor()->resolver();
- int rv = resolver->expects_pac_bytes() ?
- resolver->SetPacScriptByData(pac_script_, NULL) :
- resolver->SetPacScriptByUrl(pac_url_, NULL);
-
- DCHECK_NE(rv, ERR_IO_PENDING);
- origin_loop->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &SetPacScriptJob::RequestComplete, rv));
- }
-
- private:
- // Runs the completion callback on the origin thread.
- void RequestComplete(int result_code) {
- // The task may have been cancelled after it was started.
- if (!was_cancelled() && has_user_callback()) {
- RunUserCallback(result_code);
- }
- OnJobCompleted();
- }
-
- const GURL pac_url_;
- const string16 pac_script_;
-};
-
-// MultiThreadedProxyResolver::GetProxyForURLJob ------------------------------
-
-class MultiThreadedProxyResolver::GetProxyForURLJob
- : public MultiThreadedProxyResolver::Job {
- public:
- // |url| -- the URL of the query.
- // |results| -- the structure to fill with proxy resolve results.
- GetProxyForURLJob(const GURL& url,
- ProxyInfo* results,
- CompletionCallback* callback,
- const BoundNetLog& net_log)
- : Job(TYPE_GET_PROXY_FOR_URL, callback),
- results_(results),
- net_log_(net_log),
- url_(url),
- was_waiting_for_thread_(false) {
- DCHECK(callback);
- }
-
- BoundNetLog* net_log() { return &net_log_; }
-
- virtual void WaitingForThread() {
- was_waiting_for_thread_ = true;
- net_log_.BeginEvent(
- NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD, NULL);
- }
-
- virtual void FinishedWaitingForThread() {
- DCHECK(executor());
-
- if (was_waiting_for_thread_) {
- net_log_.EndEvent(
- NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD, NULL);
- }
-
- net_log_.AddEvent(
- NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
- new NetLogIntegerParameter(
- "thread_number", executor()->thread_number()));
- }
-
- // Runs on the worker thread.
- virtual void Run(MessageLoop* origin_loop) {
- const size_t kNetLogBound = 50u;
- worker_log_.reset(new CapturingNetLog(kNetLogBound));
- BoundNetLog bound_worker_log(NetLog::Source(), worker_log_.get());
-
- ProxyResolver* resolver = executor()->resolver();
- int rv = resolver->GetProxyForURL(
- url_, &results_buf_, NULL, NULL, bound_worker_log);
- DCHECK_NE(rv, ERR_IO_PENDING);
-
- origin_loop->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &GetProxyForURLJob::QueryComplete, rv));
- }
-
- private:
- // Runs the completion callback on the origin thread.
- void QueryComplete(int result_code) {
- // The Job may have been cancelled after it was started.
- if (!was_cancelled()) {
- // Merge the load log that was generated on the worker thread, into the
- // main log.
- CapturingBoundNetLog bound_worker_log(NetLog::Source(),
- worker_log_.release());
- bound_worker_log.AppendTo(net_log_);
-
- if (result_code >= OK) { // Note: unit-tests use values > 0.
- results_->Use(results_buf_);
- }
- RunUserCallback(result_code);
- }
- OnJobCompleted();
- }
-
- // Must only be used on the "origin" thread.
- ProxyInfo* results_;
- BoundNetLog net_log_;
- const GURL url_;
-
- // Usable from within DoQuery on the worker thread.
- ProxyInfo results_buf_;
-
- // Used to pass the captured events between DoQuery [worker thread] and
- // QueryComplete [origin thread].
- scoped_ptr<CapturingNetLog> worker_log_;
-
- bool was_waiting_for_thread_;
-};
-
-// MultiThreadedProxyResolver::Executor ----------------------------------------
-
-MultiThreadedProxyResolver::Executor::Executor(
- MultiThreadedProxyResolver* coordinator,
- ProxyResolver* resolver,
- int thread_number)
- : coordinator_(coordinator),
- thread_number_(thread_number),
- resolver_(resolver) {
- DCHECK(coordinator);
- DCHECK(resolver);
- // Start up the thread.
- // Note that it is safe to pass a temporary C-String to Thread(), as it will
- // make a copy.
- std::string thread_name =
- StringPrintf("PAC thread #%d", thread_number);
- thread_.reset(new base::Thread(thread_name.c_str()));
- thread_->Start();
-}
-
-void MultiThreadedProxyResolver::Executor::StartJob(Job* job) {
- DCHECK(!outstanding_job_);
- outstanding_job_ = job;
-
- // Run the job. Once it has completed (regardless of whether it was
- // cancelled), it will invoke OnJobCompleted() on this thread.
- job->set_executor(this);
- job->FinishedWaitingForThread();
- thread_->message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(job, &Job::Run, MessageLoop::current()));
-}
-
-void MultiThreadedProxyResolver::Executor::OnJobCompleted(Job* job) {
- DCHECK_EQ(job, outstanding_job_.get());
- outstanding_job_ = NULL;
- coordinator_->OnExecutorReady(this);
-}
-
-void MultiThreadedProxyResolver::Executor::Destroy() {
- DCHECK(coordinator_);
-
- // Give the resolver an opportunity to shutdown from THIS THREAD before
- // joining on the resolver thread. This allows certain implementations
- // to avoid deadlocks.
- resolver_->Shutdown();
-
- // Join the worker thread.
- thread_.reset();
-
- // Cancel any outstanding job.
- if (outstanding_job_) {
- outstanding_job_->Cancel();
- // Orphan the job (since this executor may be deleted soon).
- outstanding_job_->set_executor(NULL);
- }
-
- // It is now safe to free the ProxyResolver, since all the tasks that
- // were using it on the resolver thread have completed.
- resolver_.reset();
-
- // Null some stuff as a precaution.
- coordinator_ = NULL;
- outstanding_job_ = NULL;
-}
-
-void MultiThreadedProxyResolver::Executor::PurgeMemory() {
- scoped_refptr<PurgeMemoryTask> helper(new PurgeMemoryTask(resolver_.get()));
- thread_->message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(helper.get(), &PurgeMemoryTask::PurgeMemory));
-}
-
-MultiThreadedProxyResolver::Executor::~Executor() {
- // The important cleanup happens as part of Destroy(), which should always be
- // called first.
- DCHECK(!coordinator_) << "Destroy() was not called";
- DCHECK(!thread_.get());
- DCHECK(!resolver_.get());
- DCHECK(!outstanding_job_);
-}
-
-// MultiThreadedProxyResolver --------------------------------------------------
-
-MultiThreadedProxyResolver::MultiThreadedProxyResolver(
- ProxyResolverFactory* resolver_factory,
- size_t max_num_threads)
- : ProxyResolver(resolver_factory->resolvers_expect_pac_bytes()),
- resolver_factory_(resolver_factory),
- max_num_threads_(max_num_threads),
- was_set_pac_script_called_(false) {
- DCHECK_GE(max_num_threads, 1u);
-}
-
-MultiThreadedProxyResolver::~MultiThreadedProxyResolver() {
- // We will cancel all outstanding requests.
- pending_jobs_.clear();
- ReleaseAllExecutors();
-}
-
-int MultiThreadedProxyResolver::GetProxyForURL(const GURL& url,
- ProxyInfo* results,
- CompletionCallback* callback,
- RequestHandle* request,
- const BoundNetLog& net_log) {
- DCHECK(CalledOnValidThread());
- DCHECK(callback);
- DCHECK(was_set_pac_script_called_)
- << "Resolver is un-initialized. Must call SetPacScript() first!";
-
- scoped_refptr<GetProxyForURLJob> job =
- new GetProxyForURLJob(url, results, callback, net_log);
-
- // Completion will be notified through |callback|, unless the caller cancels
- // the request using |request|.
- if (request)
- *request = reinterpret_cast<RequestHandle>(job.get());
-
- // If there is an executor that is ready to run this request, submit it!
- Executor* executor = FindIdleExecutor();
- if (executor) {
- DCHECK_EQ(0u, pending_jobs_.size());
- executor->StartJob(job);
- return ERR_IO_PENDING;
- }
-
- // Otherwise queue this request. (We will schedule it to a thread once one
- // becomes available).
- job->WaitingForThread();
- pending_jobs_.push_back(job);
-
- // If we haven't already reached the thread limit, provision a new thread to
- // drain the requests more quickly.
- if (executors_.size() < max_num_threads_) {
- executor = AddNewExecutor();
- executor->StartJob(
- new SetPacScriptJob(current_pac_url_, current_pac_script_, NULL));
- }
-
- return ERR_IO_PENDING;
-}
-
-void MultiThreadedProxyResolver::CancelRequest(RequestHandle req) {
- DCHECK(CalledOnValidThread());
- DCHECK(req);
-
- Job* job = reinterpret_cast<Job*>(req);
- DCHECK_EQ(Job::TYPE_GET_PROXY_FOR_URL, job->type());
-
- if (job->executor()) {
- // If the job was already submitted to the executor, just mark it
- // as cancelled so the user callback isn't run on completion.
- job->Cancel();
- } else {
- // Otherwise the job is just sitting in a queue.
- PendingJobsQueue::iterator it =
- std::find(pending_jobs_.begin(), pending_jobs_.end(), job);
- DCHECK(it != pending_jobs_.end());
- pending_jobs_.erase(it);
- }
-}
-
-void MultiThreadedProxyResolver::CancelSetPacScript() {
- DCHECK(CalledOnValidThread());
- DCHECK_EQ(0u, pending_jobs_.size());
- DCHECK_EQ(1u, executors_.size());
- DCHECK_EQ(Job::TYPE_SET_PAC_SCRIPT,
- executors_[0]->outstanding_job()->type());
-
- // Defensively clear some data which shouldn't be getting used
- // anymore.
- was_set_pac_script_called_ = false;
- current_pac_url_ = GURL();
- current_pac_script_ = string16();
-
- ReleaseAllExecutors();
-}
-
-void MultiThreadedProxyResolver::PurgeMemory() {
- DCHECK(CalledOnValidThread());
- for (ExecutorList::iterator it = executors_.begin();
- it != executors_.end(); ++it) {
- Executor* executor = *it;
- executor->PurgeMemory();
- }
-}
-
-int MultiThreadedProxyResolver::SetPacScript(
- const GURL& pac_url,
- const string16& pac_script,
- CompletionCallback* callback) {
- DCHECK(CalledOnValidThread());
- DCHECK(callback);
-
- // Save the script details, so we can provision new executors later.
- // (We rely on internal reference counting of strings to avoid this memory
- // being duplicated by each of the resolver threads).
- was_set_pac_script_called_ = true;
- current_pac_url_ = pac_url;
- current_pac_script_ = pac_script;
-
- // The user should not have any outstanding requests when they call
- // SetPacScript().
- CheckNoOutstandingUserRequests();
-
- // Destroy all of the current threads and their proxy resolvers.
- ReleaseAllExecutors();
-
- // Provision a new executor, and run the SetPacScript request. On completion
- // notification will be sent through |callback|.
- Executor* executor = AddNewExecutor();
- executor->StartJob(new SetPacScriptJob(pac_url, pac_script, callback));
- return ERR_IO_PENDING;
-}
-
-void MultiThreadedProxyResolver::CheckNoOutstandingUserRequests() const {
- DCHECK(CalledOnValidThread());
- CHECK_EQ(0u, pending_jobs_.size());
-
- for (ExecutorList::const_iterator it = executors_.begin();
- it != executors_.end(); ++it) {
- const Executor* executor = *it;
- Job* job = executor->outstanding_job();
- // The "has_user_callback()" is to exclude jobs for which the callback
- // has already been invoked, or was not user-initiated (as in the case of
- // lazy thread provisions). User-initiated jobs may !has_user_callback()
- // when the callback has already been run. (Since we only clear the
- // outstanding job AFTER the callback has been invoked, it is possible
- // for a new request to be started from within the callback).
- CHECK(!job || job->was_cancelled() || !job->has_user_callback());
- }
-}
-
-void MultiThreadedProxyResolver::ReleaseAllExecutors() {
- DCHECK(CalledOnValidThread());
- for (ExecutorList::iterator it = executors_.begin();
- it != executors_.end(); ++it) {
- Executor* executor = *it;
- executor->Destroy();
- }
- executors_.clear();
-}
-
-MultiThreadedProxyResolver::Executor*
-MultiThreadedProxyResolver::FindIdleExecutor() {
- DCHECK(CalledOnValidThread());
- for (ExecutorList::iterator it = executors_.begin();
- it != executors_.end(); ++it) {
- Executor* executor = *it;
- if (!executor->outstanding_job())
- return executor;
- }
- return NULL;
-}
-
-MultiThreadedProxyResolver::Executor*
-MultiThreadedProxyResolver::AddNewExecutor() {
- DCHECK(CalledOnValidThread());
- DCHECK_LT(executors_.size(), max_num_threads_);
- // The "thread number" is used to give the thread a unique name.
- int thread_number = executors_.size();
- ProxyResolver* resolver = resolver_factory_->CreateProxyResolver();
- Executor* executor = new Executor(
- this, resolver, thread_number);
- executors_.push_back(executor);
- return executor;
-}
-
-void MultiThreadedProxyResolver::OnExecutorReady(Executor* executor) {
- DCHECK(CalledOnValidThread());
- if (pending_jobs_.empty())
- return;
-
- // Get the next job to process (FIFO). Transfer it from the pending queue
- // to the executor.
- scoped_refptr<Job> job = pending_jobs_.front();
- pending_jobs_.pop_front();
- executor->StartJob(job);
-}
-
-} // namespace net
diff --git a/net/proxy/multi_threaded_proxy_resolver.h b/net/proxy/multi_threaded_proxy_resolver.h
deleted file mode 100644
index c192251..0000000
--- a/net/proxy/multi_threaded_proxy_resolver.h
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright (c) 2009 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.
-
-#ifndef NET_PROXY_MULTI_THREADED_PROXY_RESOLVER_H_
-#define NET_PROXY_MULTI_THREADED_PROXY_RESOLVER_H_
-
-#include <deque>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/non_thread_safe.h"
-#include "base/ref_counted.h"
-#include "base/scoped_ptr.h"
-#include "net/proxy/proxy_resolver.h"
-
-namespace base {
-class Thread;
-} // namespace base
-
-namespace net {
-
-// ProxyResolverFactory is an interface for creating ProxyResolver instances.
-class ProxyResolverFactory {
- public:
- explicit ProxyResolverFactory(bool resolvers_expect_pac_bytes)
- : resolvers_expect_pac_bytes_(resolvers_expect_pac_bytes) {}
-
- virtual ~ProxyResolverFactory() {}
-
- // Creates a new ProxyResolver. The caller is responsible for freeing this
- // object.
- virtual ProxyResolver* CreateProxyResolver() = 0;
-
- bool resolvers_expect_pac_bytes() const {
- return resolvers_expect_pac_bytes_;
- }
-
- private:
- bool resolvers_expect_pac_bytes_;
- DISALLOW_COPY_AND_ASSIGN(ProxyResolverFactory);
-};
-
-// MultiThreadedProxyResolver is a ProxyResolver implementation that runs
-// synchronous ProxyResolver implementations on worker threads.
-//
-// Threads are created lazily on demand, up to a maximum total. The advantage
-// of having a pool of threads, is faster performance. In particular, being
-// able to keep servicing PAC requests even if one blocks its execution.
-//
-// During initialization (SetPacScript), a single thread is spun up to test
-// the script. If this succeeds, we cache the input script, and will re-use
-// this to lazily provision any new threads as needed.
-//
-// For each new thread that we spawn, a corresponding new ProxyResolver is
-// created using ProxyResolverFactory.
-//
-// Because we are creating multiple ProxyResolver instances, this means we
-// are duplicating script contexts for what is ordinarily seen as being a
-// single script. This can affect compatibility on some classes of PAC
-// script:
-//
-// (a) Scripts whose initialization has external dependencies on network or
-// time may end up successfully initializing on some threads, but not
-// others. So depending on what thread services the request, the result
-// may jump between several possibilities.
-//
-// (b) Scripts whose FindProxyForURL() depends on side-effects may now
-// work differently. For example, a PAC script which was incrementing
-// a global counter and using that to make a decision. In the
-// multi-threaded model, each thread may have a different value for this
-// counter, so it won't globally be seen as monotonically increasing!
-class MultiThreadedProxyResolver : public ProxyResolver, public NonThreadSafe {
- public:
- // Creates an asynchronous ProxyResolver that runs requests on up to
- // |max_num_threads|.
- //
- // For each thread that is created, an accompanying synchronous ProxyResolver
- // will be provisioned using |resolver_factory|. All methods on these
- // ProxyResolvers will be called on the one thread, with the exception of
- // ProxyResolver::Shutdown() which will be called from the origin thread
- // prior to destruction.
- //
- // The constructor takes ownership of |resolver_factory|.
- MultiThreadedProxyResolver(ProxyResolverFactory* resolver_factory,
- size_t max_num_threads);
-
- virtual ~MultiThreadedProxyResolver();
-
- // ProxyResolver implementation:
- virtual int GetProxyForURL(const GURL& url,
- ProxyInfo* results,
- CompletionCallback* callback,
- RequestHandle* request,
- const BoundNetLog& net_log);
- virtual void CancelRequest(RequestHandle request);
- virtual void CancelSetPacScript();
- virtual void PurgeMemory();
-
- private:
- class Executor;
- class Job;
- class SetPacScriptJob;
- class GetProxyForURLJob;
- // FIFO queue of pending jobs waiting to be started.
- // TODO(eroman): Make this priority queue.
- typedef std::deque<scoped_refptr<Job> > PendingJobsQueue;
- typedef std::vector<scoped_refptr<Executor> > ExecutorList;
-
- // ProxyResolver implementation:
- virtual int SetPacScript(const GURL& pac_url,
- const string16& pac_script,
- CompletionCallback* callback);
-
- // Asserts that there are no outstanding user-initiated jobs on any of the
- // worker threads.
- void CheckNoOutstandingUserRequests() const;
-
- // Stops and deletes all of the worker threads.
- void ReleaseAllExecutors();
-
- // Returns an idle worker thread which is ready to receive GetProxyForURL()
- // requests. If all threads are occupied, returns NULL.
- Executor* FindIdleExecutor();
-
- // Creates a new worker thread, and appends it to |executors_|.
- Executor* AddNewExecutor();
-
- // Starts the next job from |pending_jobs_| if possible.
- void OnExecutorReady(Executor* executor);
-
- const scoped_ptr<ProxyResolverFactory> resolver_factory_;
- const size_t max_num_threads_;
- PendingJobsQueue pending_jobs_;
- ExecutorList executors_;
- bool was_set_pac_script_called_;
- GURL current_pac_url_;
- string16 current_pac_script_;
-};
-
-} // namespace net
-
-#endif // NET_PROXY_MULTI_THREADED_PROXY_RESOLVER_H_
diff --git a/net/proxy/proxy_resolver.h b/net/proxy/proxy_resolver.h
index 37b1a3a..b200206 100644
--- a/net/proxy/proxy_resolver.h
+++ b/net/proxy/proxy_resolver.h
@@ -74,10 +74,6 @@ class ProxyResolver {
// no-op implementation.
virtual void PurgeMemory() {}
- // Optional shutdown code to be run before destruction. This is only used
- // by the multithreaded runner to signal cleanup from origin thread
- virtual void Shutdown() {}
-
private:
// Called to set the PAC script backend to use. If |pac_url| is invalid,
// this is a request to use WPAD (auto detect). |pac_script| may be empty if
diff --git a/net/proxy/proxy_resolver_js_bindings.cc b/net/proxy/proxy_resolver_js_bindings.cc
index 08a7fe7..3bf1bad 100644
--- a/net/proxy/proxy_resolver_js_bindings.cc
+++ b/net/proxy/proxy_resolver_js_bindings.cc
@@ -110,12 +110,8 @@ class DefaultJSBindings : public ProxyResolverJSBindings {
LOG(INFO) << "PAC-error: " << "line: " << line_number << ": " << message;
}
- virtual void Shutdown() {
- host_resolver_->Shutdown();
- }
-
private:
- // Helper to execute a synchronous DNS resolve, using the per-request
+ // Helper to execute a syncrhonous DNS resolve, using the per-request
// DNS cache if there is one.
int DnsResolveHelper(const HostResolver::RequestInfo& info,
AddressList* address_list) {
diff --git a/net/proxy/proxy_resolver_js_bindings.h b/net/proxy/proxy_resolver_js_bindings.h
index b5912c4..834ea73 100644
--- a/net/proxy/proxy_resolver_js_bindings.h
+++ b/net/proxy/proxy_resolver_js_bindings.h
@@ -54,9 +54,6 @@ class ProxyResolverJSBindings {
// if a line number is not applicable to this error.
virtual void OnError(int line_number, const string16& error) = 0;
- // Called before the thread running the proxy resolver is stopped.
- virtual void Shutdown() = 0;
-
// Creates a default javascript bindings implementation that will:
// - Send script error messages to LOG(INFO)
// - Send script alert()s to LOG(INFO)
diff --git a/net/proxy/proxy_resolver_v8.cc b/net/proxy/proxy_resolver_v8.cc
index 4a47b345..5860872 100644
--- a/net/proxy/proxy_resolver_v8.cc
+++ b/net/proxy/proxy_resolver_v8.cc
@@ -640,10 +640,6 @@ void ProxyResolverV8::PurgeMemory() {
context_->PurgeMemory();
}
-void ProxyResolverV8::Shutdown() {
- js_bindings_->Shutdown();
-}
-
int ProxyResolverV8::SetPacScript(const GURL& /*url*/,
const string16& pac_script,
CompletionCallback* /*callback*/) {
diff --git a/net/proxy/proxy_resolver_v8.h b/net/proxy/proxy_resolver_v8.h
index 20f31fc..bc4d731 100644
--- a/net/proxy/proxy_resolver_v8.h
+++ b/net/proxy/proxy_resolver_v8.h
@@ -50,7 +50,6 @@ class ProxyResolverV8 : public ProxyResolver {
const BoundNetLog& net_log);
virtual void CancelRequest(RequestHandle request);
virtual void PurgeMemory();
- virtual void Shutdown();
ProxyResolverJSBindings* js_bindings() const { return js_bindings_.get(); }
diff --git a/net/proxy/proxy_resolver_v8_unittest.cc b/net/proxy/proxy_resolver_v8_unittest.cc
index ae664b2..2e09e4a 100644
--- a/net/proxy/proxy_resolver_v8_unittest.cc
+++ b/net/proxy/proxy_resolver_v8_unittest.cc
@@ -62,8 +62,6 @@ class MockJSBindings : public ProxyResolverJSBindings {
errors_line_number.push_back(line_number);
}
- virtual void Shutdown() {}
-
// Mock values to return.
std::string my_ip_address_result;
std::string my_ip_address_ex_result;
diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc
index 060745b..26cb1b6 100644
--- a/net/proxy/proxy_service.cc
+++ b/net/proxy/proxy_service.cc
@@ -15,7 +15,6 @@
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/proxy/init_proxy_resolver.h"
-#include "net/proxy/multi_threaded_proxy_resolver.h"
#include "net/proxy/proxy_config_service_fixed.h"
#include "net/proxy/proxy_script_fetcher.h"
#if defined(OS_WIN)
@@ -30,6 +29,7 @@
#include "net/proxy/proxy_resolver.h"
#include "net/proxy/proxy_resolver_js_bindings.h"
#include "net/proxy/proxy_resolver_v8.h"
+#include "net/proxy/single_threaded_proxy_resolver.h"
#include "net/proxy/sync_host_resolver_bridge.h"
#include "net/url_request/url_request_context.h"
@@ -75,55 +75,6 @@ class ProxyResolverNull : public ProxyResolver {
}
};
-// This factory creates V8ProxyResolvers with appropriate javascript bindings.
-class ProxyResolverFactoryForV8 : public ProxyResolverFactory {
- public:
- // Both |async_host_resolver| and |host_resolver_loop| must remain valid for
- // duration of our lifetime.
- ProxyResolverFactoryForV8(HostResolver* async_host_resolver,
- MessageLoop* host_resolver_loop)
- : ProxyResolverFactory(true /*expects_pac_bytes*/),
- async_host_resolver_(async_host_resolver),
- host_resolver_loop_(host_resolver_loop) {
- }
-
- virtual ProxyResolver* CreateProxyResolver() {
- // Create a synchronous host resolver wrapper that operates
- // |async_host_resolver_| on |host_resolver_loop_|.
- SyncHostResolverBridge* sync_host_resolver =
- new SyncHostResolverBridge(async_host_resolver_, host_resolver_loop_);
-
- ProxyResolverJSBindings* js_bindings =
- ProxyResolverJSBindings::CreateDefault(sync_host_resolver);
-
- // ProxyResolverV8 takes ownership of |js_bindings|.
- return new ProxyResolverV8(js_bindings);
- }
-
- private:
- scoped_refptr<HostResolver> async_host_resolver_;
- MessageLoop* host_resolver_loop_;
-};
-
-// Creates ProxyResolvers using a non-V8 implementation.
-class ProxyResolverFactoryForNonV8 : public ProxyResolverFactory {
- public:
- ProxyResolverFactoryForNonV8()
- : ProxyResolverFactory(false /*expects_pac_bytes*/) {}
-
- virtual ProxyResolver* CreateProxyResolver() {
-#if defined(OS_WIN)
- return new ProxyResolverWinHttp();
-#elif defined(OS_MACOSX)
- return new ProxyResolverMac();
-#else
- LOG(WARNING) << "PAC support disabled because there is no fallback "
- "non-V8 implementation";
- return new ProxyResolverNull();
-#endif
- }
-};
-
// ProxyService::PacRequest ---------------------------------------------------
class ProxyService::PacRequest
@@ -267,22 +218,32 @@ ProxyService* ProxyService::Create(
URLRequestContext* url_request_context,
NetLog* net_log,
MessageLoop* io_loop) {
+ ProxyResolver* proxy_resolver = NULL;
- ProxyResolverFactory* sync_resolver_factory;
if (use_v8_resolver) {
- sync_resolver_factory =
- new ProxyResolverFactoryForV8(
- url_request_context->host_resolver(),
- io_loop);
+ // Use the IO thread's host resolver (but since it is not threadsafe,
+ // bridge requests from the PAC thread over to the IO thread).
+ SyncHostResolverBridge* sync_host_resolver =
+ new SyncHostResolverBridge(url_request_context->host_resolver(),
+ io_loop);
+
+ // Send javascript errors and alerts to LOG(INFO).
+ ProxyResolverJSBindings* js_bindings =
+ ProxyResolverJSBindings::CreateDefault(sync_host_resolver);
+
+ // Wrap the (synchronous) ProxyResolver implementation in a single-threaded
+ // asynchronous resolver. This version of SingleThreadedProxyResolver
+ // additionally aborts any synchronous host resolves to avoid deadlock
+ // during shutdown.
+ proxy_resolver =
+ new SingleThreadedProxyResolverUsingBridgedHostResolver(
+ new ProxyResolverV8(js_bindings),
+ sync_host_resolver);
} else {
- sync_resolver_factory = new ProxyResolverFactoryForNonV8();
+ proxy_resolver =
+ new SingleThreadedProxyResolver(CreateNonV8ProxyResolver());
}
- const size_t kMaxNumResolverThreads = 1u;
- ProxyResolver* proxy_resolver =
- new MultiThreadedProxyResolver(sync_resolver_factory,
- kMaxNumResolverThreads);
-
ProxyService* proxy_service =
new ProxyService(proxy_config_service, proxy_resolver, net_log);
@@ -598,6 +559,19 @@ ProxyConfigService* ProxyService::CreateSystemProxyConfigService(
#endif
}
+// static
+ProxyResolver* ProxyService::CreateNonV8ProxyResolver() {
+#if defined(OS_WIN)
+ return new ProxyResolverWinHttp();
+#elif defined(OS_MACOSX)
+ return new ProxyResolverMac();
+#else
+ LOG(WARNING) << "PAC support disabled because there is no fallback "
+ "non-V8 implementation";
+ return new ProxyResolverNull();
+#endif
+}
+
void ProxyService::UpdateConfig(const BoundNetLog& net_log) {
bool is_first_update = !config_has_been_initialized();
diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h
index 5c981f8..22435e4 100644
--- a/net/proxy/proxy_service.h
+++ b/net/proxy/proxy_service.h
@@ -181,6 +181,10 @@ class ProxyService : public base::RefCountedThreadSafe<ProxyService>,
~ProxyService();
+ // Creates a proxy resolver appropriate for this platform that doesn't rely
+ // on V8.
+ static ProxyResolver* CreateNonV8ProxyResolver();
+
// Identifies the proxy configuration.
ProxyConfig::ID config_id() const { return config_.id(); }
diff --git a/net/proxy/single_threaded_proxy_resolver.cc b/net/proxy/single_threaded_proxy_resolver.cc
new file mode 100644
index 0000000..2fdc3ac
--- /dev/null
+++ b/net/proxy/single_threaded_proxy_resolver.cc
@@ -0,0 +1,353 @@
+// Copyright (c) 2010 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.
+
+#include "net/proxy/single_threaded_proxy_resolver.h"
+
+#include "base/message_loop.h"
+#include "base/thread.h"
+#include "net/base/capturing_net_log.h"
+#include "net/base/net_errors.h"
+#include "net/proxy/proxy_info.h"
+
+namespace net {
+
+namespace {
+
+class PurgeMemoryTask : public base::RefCountedThreadSafe<PurgeMemoryTask> {
+ public:
+ explicit PurgeMemoryTask(ProxyResolver* resolver) : resolver_(resolver) {}
+ void PurgeMemory() { resolver_->PurgeMemory(); }
+ private:
+ friend class base::RefCountedThreadSafe<PurgeMemoryTask>;
+ ~PurgeMemoryTask() {}
+ ProxyResolver* resolver_;
+};
+
+} // namespace
+
+// SingleThreadedProxyResolver::SetPacScriptTask ------------------------------
+
+// Runs on the worker thread to call ProxyResolver::SetPacScript.
+class SingleThreadedProxyResolver::SetPacScriptTask
+ : public base::RefCountedThreadSafe<
+ SingleThreadedProxyResolver::SetPacScriptTask> {
+ public:
+ SetPacScriptTask(SingleThreadedProxyResolver* coordinator,
+ const GURL& pac_url,
+ const string16& pac_script,
+ CompletionCallback* callback)
+ : coordinator_(coordinator),
+ callback_(callback),
+ pac_script_(pac_script),
+ pac_url_(pac_url),
+ origin_loop_(MessageLoop::current()) {
+ DCHECK(callback);
+ }
+
+ // Start the SetPacScript request on the worker thread.
+ void Start() {
+ coordinator_->thread()->message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &SetPacScriptTask::DoRequest,
+ coordinator_->resolver_.get()));
+ }
+
+ void Cancel() {
+ // Clear these to inform RequestComplete that it should not try to
+ // access them.
+ coordinator_ = NULL;
+ callback_ = NULL;
+ }
+
+ // Returns true if Cancel() has been called.
+ bool was_cancelled() const { return callback_ == NULL; }
+
+ private:
+ friend class base::RefCountedThreadSafe<
+ SingleThreadedProxyResolver::SetPacScriptTask>;
+
+ ~SetPacScriptTask() {}
+
+ // Runs on the worker thread.
+ void DoRequest(ProxyResolver* resolver) {
+ int rv = resolver->expects_pac_bytes() ?
+ resolver->SetPacScriptByData(pac_script_, NULL) :
+ resolver->SetPacScriptByUrl(pac_url_, NULL);
+
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ origin_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &SetPacScriptTask::RequestComplete, rv));
+ }
+
+ // Runs the completion callback on the origin thread.
+ void RequestComplete(int result_code) {
+ // The task may have been cancelled after it was started.
+ if (!was_cancelled()) {
+ CompletionCallback* callback = callback_;
+ coordinator_->RemoveOutstandingSetPacScriptTask(this);
+ callback->Run(result_code);
+ }
+ }
+
+ // Must only be used on the "origin" thread.
+ SingleThreadedProxyResolver* coordinator_;
+ CompletionCallback* callback_;
+ string16 pac_script_;
+ GURL pac_url_;
+
+ // Usable from within DoQuery on the worker thread.
+ MessageLoop* origin_loop_;
+};
+
+// SingleThreadedProxyResolver::Job -------------------------------------------
+
+class SingleThreadedProxyResolver::Job
+ : public base::RefCountedThreadSafe<SingleThreadedProxyResolver::Job> {
+ public:
+ // |coordinator| -- the SingleThreadedProxyResolver that owns this job.
+ // |url| -- the URL of the query.
+ // |results| -- the structure to fill with proxy resolve results.
+ Job(SingleThreadedProxyResolver* coordinator,
+ const GURL& url,
+ ProxyInfo* results,
+ CompletionCallback* callback,
+ const BoundNetLog& net_log)
+ : coordinator_(coordinator),
+ callback_(callback),
+ results_(results),
+ net_log_(net_log),
+ url_(url),
+ is_started_(false),
+ origin_loop_(MessageLoop::current()) {
+ DCHECK(callback);
+ }
+
+ // Start the resolve proxy request on the worker thread.
+ void Start() {
+ is_started_ = true;
+
+ size_t load_log_bound = 100;
+
+ coordinator_->thread()->message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &Job::DoQuery,
+ coordinator_->resolver_.get(),
+ load_log_bound));
+ }
+
+ bool is_started() const { return is_started_; }
+
+ void Cancel() {
+ // Clear these to inform QueryComplete that it should not try to
+ // access them.
+ coordinator_ = NULL;
+ callback_ = NULL;
+ results_ = NULL;
+ }
+
+ // Returns true if Cancel() has been called.
+ bool was_cancelled() const { return callback_ == NULL; }
+
+ BoundNetLog* net_log() { return &net_log_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<SingleThreadedProxyResolver::Job>;
+
+ ~Job() {}
+
+ // Runs on the worker thread.
+ void DoQuery(ProxyResolver* resolver, size_t load_log_bound) {
+ worker_log_.reset(new CapturingNetLog(load_log_bound));
+ BoundNetLog bound_worker_log(NetLog::Source(), worker_log_.get());
+
+ int rv = resolver->GetProxyForURL(url_, &results_buf_, NULL, NULL,
+ bound_worker_log);
+ DCHECK_NE(rv, ERR_IO_PENDING);
+
+ origin_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &Job::QueryComplete, rv));
+ }
+
+ // Runs the completion callback on the origin thread.
+ void QueryComplete(int result_code) {
+ // Merge the load log that was generated on the worker thread, into the
+ // main log.
+ CapturingBoundNetLog bound_worker_log(NetLog::Source(),
+ worker_log_.release());
+ bound_worker_log.AppendTo(net_log_);
+
+ // The Job may have been cancelled after it was started.
+ if (!was_cancelled()) {
+ if (result_code >= OK) { // Note: unit-tests use values > 0.
+ results_->Use(results_buf_);
+ }
+ callback_->Run(result_code);
+
+ // We check for cancellation once again, in case the callback deleted
+ // the owning ProxyService (whose destructor will in turn cancel us).
+ if (!was_cancelled())
+ coordinator_->RemoveFrontOfJobsQueueAndStartNext(this);
+ }
+ }
+
+ // Must only be used on the "origin" thread.
+ SingleThreadedProxyResolver* coordinator_;
+ CompletionCallback* callback_;
+ ProxyInfo* results_;
+ BoundNetLog net_log_;
+ GURL url_;
+ bool is_started_;
+
+ // Usable from within DoQuery on the worker thread.
+ ProxyInfo results_buf_;
+ MessageLoop* origin_loop_;
+
+ // Used to pass the captured events between DoQuery [worker thread] and
+ // QueryComplete [origin thread].
+ scoped_ptr<CapturingNetLog> worker_log_;
+};
+
+// SingleThreadedProxyResolver ------------------------------------------------
+
+SingleThreadedProxyResolver::SingleThreadedProxyResolver(
+ ProxyResolver* resolver)
+ : ProxyResolver(resolver->expects_pac_bytes()),
+ resolver_(resolver) {
+}
+
+SingleThreadedProxyResolver::~SingleThreadedProxyResolver() {
+ // Cancel the inprogress job (if any), and free the rest.
+ for (PendingJobsQueue::iterator it = pending_jobs_.begin();
+ it != pending_jobs_.end();
+ ++it) {
+ (*it)->Cancel();
+ }
+
+ if (outstanding_set_pac_script_task_)
+ outstanding_set_pac_script_task_->Cancel();
+
+ // Note that |thread_| is destroyed before |resolver_|. This is important
+ // since |resolver_| could be running on |thread_|.
+}
+
+int SingleThreadedProxyResolver::GetProxyForURL(const GURL& url,
+ ProxyInfo* results,
+ CompletionCallback* callback,
+ RequestHandle* request,
+ const BoundNetLog& net_log) {
+ DCHECK(callback);
+
+ scoped_refptr<Job> job = new Job(this, url, results, callback, net_log);
+ bool is_first_job = pending_jobs_.empty();
+ pending_jobs_.push_back(job); // Jobs can never finish synchronously.
+
+ if (is_first_job) {
+ // If there is nothing already running, start the job now.
+ EnsureThreadStarted();
+ job->Start();
+ } else {
+ // Otherwise the job will get started eventually by ProcessPendingJobs().
+ job->net_log()->BeginEvent(
+ NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD, NULL);
+ }
+
+ // Completion will be notified through |callback|, unless the caller cancels
+ // the request using |request|.
+ if (request)
+ *request = reinterpret_cast<RequestHandle>(job.get());
+
+ return ERR_IO_PENDING;
+}
+
+// There are three states of the request we need to handle:
+// (1) Not started (just sitting in the queue).
+// (2) Executing Job::DoQuery in the worker thread.
+// (3) Waiting for Job::QueryComplete to be run on the origin thread.
+void SingleThreadedProxyResolver::CancelRequest(RequestHandle req) {
+ DCHECK(req);
+
+ Job* job = reinterpret_cast<Job*>(req);
+
+ bool is_active_job = job->is_started() && !pending_jobs_.empty() &&
+ pending_jobs_.front().get() == job;
+
+ job->Cancel();
+
+ if (is_active_job) {
+ RemoveFrontOfJobsQueueAndStartNext(job);
+ return;
+ }
+
+ // Otherwise just delete the job from the queue.
+ PendingJobsQueue::iterator it = std::find(
+ pending_jobs_.begin(), pending_jobs_.end(), job);
+ DCHECK(it != pending_jobs_.end());
+ pending_jobs_.erase(it);
+}
+
+void SingleThreadedProxyResolver::CancelSetPacScript() {
+ DCHECK(outstanding_set_pac_script_task_);
+ outstanding_set_pac_script_task_->Cancel();
+ outstanding_set_pac_script_task_ = NULL;
+}
+
+void SingleThreadedProxyResolver::PurgeMemory() {
+ if (thread_.get()) {
+ scoped_refptr<PurgeMemoryTask> helper(new PurgeMemoryTask(resolver_.get()));
+ thread_->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(helper.get(), &PurgeMemoryTask::PurgeMemory));
+ }
+}
+
+int SingleThreadedProxyResolver::SetPacScript(
+ const GURL& pac_url,
+ const string16& pac_script,
+ CompletionCallback* callback) {
+ EnsureThreadStarted();
+ DCHECK(!outstanding_set_pac_script_task_);
+
+ SetPacScriptTask* task = new SetPacScriptTask(
+ this, pac_url, pac_script, callback);
+ outstanding_set_pac_script_task_ = task;
+ task->Start();
+ return ERR_IO_PENDING;
+}
+
+void SingleThreadedProxyResolver::EnsureThreadStarted() {
+ if (!thread_.get()) {
+ thread_.reset(new base::Thread("pac-thread"));
+ thread_->Start();
+ }
+}
+
+void SingleThreadedProxyResolver::ProcessPendingJobs() {
+ if (pending_jobs_.empty())
+ return;
+
+ // Get the next job to process (FIFO).
+ Job* job = pending_jobs_.front().get();
+ if (job->is_started())
+ return;
+
+ job->net_log()->EndEvent(
+ NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD, NULL);
+
+ EnsureThreadStarted();
+ job->Start();
+}
+
+void SingleThreadedProxyResolver::RemoveFrontOfJobsQueueAndStartNext(
+ Job* expected_job) {
+ DCHECK_EQ(expected_job, pending_jobs_.front().get());
+ pending_jobs_.pop_front();
+
+ // Start next work item.
+ ProcessPendingJobs();
+}
+
+void SingleThreadedProxyResolver::RemoveOutstandingSetPacScriptTask(
+ SetPacScriptTask* task) {
+ DCHECK_EQ(outstanding_set_pac_script_task_.get(), task);
+ outstanding_set_pac_script_task_ = NULL;
+}
+
+} // namespace net
diff --git a/net/proxy/single_threaded_proxy_resolver.h b/net/proxy/single_threaded_proxy_resolver.h
new file mode 100644
index 0000000..1c9582c
--- /dev/null
+++ b/net/proxy/single_threaded_proxy_resolver.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2009 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.
+
+#ifndef NET_PROXY_SINGLE_THREADED_PROXY_RESOLVER_H_
+#define NET_PROXY_SINGLE_THREADED_PROXY_RESOLVER_H_
+
+#include <deque>
+#include <string>
+
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "net/proxy/proxy_resolver.h"
+
+namespace base {
+class Thread;
+} // namespace base
+
+namespace net {
+
+// ProxyResolver implementation that wraps a synchronous ProxyResolver, and
+// runs it on a single worker thread. If multiple requests accumulate, they
+// are serviced in FIFO order.
+class SingleThreadedProxyResolver : public ProxyResolver {
+ public:
+ // |resolver| is a synchronous ProxyResolver implementation. It doesn't
+ // have to be thread-safe, since it is run on exactly one thread. The
+ // constructor takes ownership of |resolver|.
+ explicit SingleThreadedProxyResolver(ProxyResolver* resolver);
+
+ virtual ~SingleThreadedProxyResolver();
+
+ // ProxyResolver implementation:
+ virtual int GetProxyForURL(const GURL& url,
+ ProxyInfo* results,
+ CompletionCallback* callback,
+ RequestHandle* request,
+ const BoundNetLog& net_log);
+ virtual void CancelRequest(RequestHandle request);
+ virtual void CancelSetPacScript();
+ virtual void PurgeMemory();
+
+ protected:
+ // The wrapped (synchronous) ProxyResolver.
+ ProxyResolver* resolver() { return resolver_.get(); }
+
+ private:
+ // Refcounted helper class that bridges between origin thread and worker
+ // thread.
+ class Job;
+ friend class Job;
+ class SetPacScriptTask;
+ friend class SetPacScriptTask;
+ // FIFO queue that contains the in-progress job, and any pending jobs.
+ typedef std::deque<scoped_refptr<Job> > PendingJobsQueue;
+
+ base::Thread* thread() { return thread_.get(); }
+
+ // ProxyResolver implementation:
+ virtual int SetPacScript(const GURL& pac_url,
+ const string16& pac_script,
+ CompletionCallback* callback);
+
+ // Starts the worker thread if it isn't already running.
+ void EnsureThreadStarted();
+
+ // Starts the next job from |pending_jobs_| if possible.
+ void ProcessPendingJobs();
+
+ // Removes the front entry of the jobs queue. |expected_job| is our
+ // expectation of what the front of the job queue is; it is only used by
+ // DCHECK for verification purposes.
+ void RemoveFrontOfJobsQueueAndStartNext(Job* expected_job);
+
+ // Clears |outstanding_set_pac_script_task_|.
+ // Called when |task| has just finished.
+ void RemoveOutstandingSetPacScriptTask(SetPacScriptTask* task);
+
+ // The synchronous resolver implementation.
+ scoped_ptr<ProxyResolver> resolver_;
+
+ // The thread where |resolver_| is run on.
+ // Note that declaration ordering is important here. |thread_| needs to be
+ // destroyed *before* |resolver_|, in case |resolver_| is currently
+ // executing on |thread_|.
+ scoped_ptr<base::Thread> thread_;
+
+ PendingJobsQueue pending_jobs_;
+ scoped_refptr<SetPacScriptTask> outstanding_set_pac_script_task_;
+};
+
+} // namespace net
+
+#endif // NET_PROXY_SINGLE_THREADED_PROXY_RESOLVER_H_
diff --git a/net/proxy/multi_threaded_proxy_resolver_unittest.cc b/net/proxy/single_threaded_proxy_resolver_unittest.cc
index f451c3a..a84253c 100644
--- a/net/proxy/multi_threaded_proxy_resolver_unittest.cc
+++ b/net/proxy/single_threaded_proxy_resolver_unittest.cc
@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/proxy/multi_threaded_proxy_resolver.h"
-
-#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "base/waitable_event.h"
#include "googleurl/src/gurl.h"
@@ -13,14 +10,14 @@
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/proxy/proxy_info.h"
+#include "net/proxy/single_threaded_proxy_resolver.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
-
namespace {
// A synchronous mock ProxyResolver implementation, which can be used in
-// conjunction with MultiThreadedProxyResolver.
+// conjunction with SingleThreadedProxyResolver.
// - returns a single-item proxy list with the query's host.
class MockProxyResolver : public ProxyResolver {
public:
@@ -72,7 +69,6 @@ class MockProxyResolver : public ProxyResolver {
}
int purge_count() const { return purge_count_; }
- int request_count() const { return request_count_; }
const string16& last_pac_script() const { return last_pac_script_; }
@@ -83,7 +79,7 @@ class MockProxyResolver : public ProxyResolver {
private:
void CheckIsOnWorkerThread() {
// We should be running on the worker thread -- while we don't know the
- // message loop of MultiThreadedProxyResolver's worker thread, we do
+ // message loop of SingleThreadedProxyResolver's worker thread, we do
// know that it is going to be distinct from the loop running the
// test, so at least make sure it isn't the main loop.
EXPECT_NE(MessageLoop::current(), wrong_loop_);
@@ -145,87 +141,9 @@ class BlockableProxyResolver : public MockProxyResolver {
base::WaitableEvent blocked_;
};
-// ForwardingProxyResolver forwards all requests to |impl|.
-class ForwardingProxyResolver : public ProxyResolver {
- public:
- explicit ForwardingProxyResolver(ProxyResolver* impl)
- : ProxyResolver(impl->expects_pac_bytes()),
- impl_(impl) {}
-
- virtual int GetProxyForURL(const GURL& query_url,
- ProxyInfo* results,
- CompletionCallback* callback,
- RequestHandle* request,
- const BoundNetLog& net_log) {
- return impl_->GetProxyForURL(
- query_url, results, callback, request, net_log);
- }
-
- virtual void CancelRequest(RequestHandle request) {
- impl_->CancelRequest(request);
- }
-
- virtual int SetPacScript(const GURL& pac_url,
- const string16& script,
- CompletionCallback* callback) {
- if (impl_->expects_pac_bytes())
- return impl_->SetPacScriptByData(script, callback);
- else
- return impl_->SetPacScriptByUrl(pac_url, callback);
- }
-
- virtual void PurgeMemory() {
- impl_->PurgeMemory();
- }
-
- private:
- ProxyResolver* impl_;
-};
-
-// This factory returns ProxyResolvers that forward all requests to
-// |resolver|.
-class ForwardingProxyResolverFactory : public ProxyResolverFactory {
- public:
- explicit ForwardingProxyResolverFactory(ProxyResolver* resolver)
- : ProxyResolverFactory(resolver->expects_pac_bytes()),
- resolver_(resolver) {}
-
- virtual ProxyResolver* CreateProxyResolver() {
- return new ForwardingProxyResolver(resolver_);
- }
-
- private:
- ProxyResolver* resolver_;
-};
-
-// This factory returns new instances of BlockableProxyResolver.
-class BlockableProxyResolverFactory : public ProxyResolverFactory {
- public:
- BlockableProxyResolverFactory() : ProxyResolverFactory(true) {}
-
- ~BlockableProxyResolverFactory() {
- STLDeleteElements(&resolvers_);
- }
-
- virtual ProxyResolver* CreateProxyResolver() {
- BlockableProxyResolver* resolver = new BlockableProxyResolver;
- resolvers_.push_back(resolver);
- return new ForwardingProxyResolver(resolver);
- }
-
- std::vector<BlockableProxyResolver*> resolvers() {
- return resolvers_;
- }
-
- private:
- std::vector<BlockableProxyResolver*> resolvers_;
-};
-
-TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) {
- const size_t kNumThreads = 1u;
- scoped_ptr<MockProxyResolver> mock(new MockProxyResolver);
- MultiThreadedProxyResolver resolver(
- new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
+TEST(SingleThreadedProxyResolverTest, Basic) {
+ MockProxyResolver* mock = new MockProxyResolver;
+ SingleThreadedProxyResolver resolver(mock);
int rv;
@@ -255,11 +173,7 @@ TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) {
// The mock proxy resolver should have written 1 log entry. And
// on completion, this should have been copied into |log0|.
- // We also have 1 log entry that was emitted by the
- // MultiThreadedProxyResolver.
- ASSERT_EQ(2u, log0.entries().size());
- EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
- log0.entries()[0].type);
+ EXPECT_EQ(1u, log0.entries().size());
// Start 3 more requests (request1 to request3).
@@ -311,20 +225,12 @@ TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) {
// Tests that the NetLog is updated to include the time the request was waiting
// to be scheduled to a thread.
-TEST(MultiThreadedProxyResolverTest,
- SingleThread_UpdatesNetLogWithThreadWait) {
- const size_t kNumThreads = 1u;
- scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
- MultiThreadedProxyResolver resolver(
- new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
+TEST(SingleThreadedProxyResolverTest, UpdatesNetLogWithThreadWait) {
+ BlockableProxyResolver* mock = new BlockableProxyResolver;
+ SingleThreadedProxyResolver resolver(mock);
int rv;
- // Initialize the resolver.
- TestCompletionCallback init_callback;
- rv = resolver.SetPacScriptByData(ASCIIToUTF16("foo"), &init_callback);
- EXPECT_EQ(OK, init_callback.WaitForResult());
-
// Block the proxy resolver, so no request can complete.
mock->Block();
@@ -359,53 +265,42 @@ TEST(MultiThreadedProxyResolverTest,
mock->Unblock();
// Check that request 0 completed as expected.
- // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and
- // 1 entry from the mock proxy resolver.
+ // The NetLog only has 1 entry (that came from the mock proxy resolver.)
EXPECT_EQ(0, callback0.WaitForResult());
EXPECT_EQ("PROXY request0:80", results0.ToPacString());
- ASSERT_EQ(2u, log0.entries().size());
- EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
- log0.entries()[0].type);
+ ASSERT_EQ(1u, log0.entries().size());
// Check that request 1 completed as expected.
EXPECT_EQ(1, callback1.WaitForResult());
EXPECT_EQ("PROXY request1:80", results1.ToPacString());
- ASSERT_EQ(4u, log1.entries().size());
+ ASSERT_EQ(3u, log1.entries().size());
EXPECT_TRUE(LogContainsBeginEvent(
log1.entries(), 0,
- NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
+ NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD));
EXPECT_TRUE(LogContainsEndEvent(
log1.entries(), 1,
- NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
+ NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD));
// Check that request 2 completed as expected.
EXPECT_EQ(2, callback2.WaitForResult());
EXPECT_EQ("PROXY request2:80", results2.ToPacString());
- ASSERT_EQ(4u, log2.entries().size());
+ ASSERT_EQ(3u, log2.entries().size());
EXPECT_TRUE(LogContainsBeginEvent(
log2.entries(), 0,
- NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
+ NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD));
EXPECT_TRUE(LogContainsEndEvent(
log2.entries(), 1,
- NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
+ NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD));
}
// Cancel a request which is in progress, and then cancel a request which
// is pending.
-TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) {
- const size_t kNumThreads = 1u;
- scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
- MultiThreadedProxyResolver resolver(
- new ForwardingProxyResolverFactory(mock.get()),
- kNumThreads);
+TEST(SingleThreadedProxyResolverTest, CancelRequest) {
+ BlockableProxyResolver* mock = new BlockableProxyResolver;
+ SingleThreadedProxyResolver resolver(mock);
int rv;
- // Initialize the resolver.
- TestCompletionCallback init_callback;
- rv = resolver.SetPacScriptByData(ASCIIToUTF16("foo"), &init_callback);
- EXPECT_EQ(OK, init_callback.WaitForResult());
-
// Block the proxy resolver, so no request can complete.
mock->Block();
@@ -466,22 +361,15 @@ TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) {
EXPECT_FALSE(callback2.have_result());
}
-// Test that deleting MultiThreadedProxyResolver while requests are
+// Test that deleting SingleThreadedProxyResolver while requests are
// outstanding cancels them (and doesn't leak anything).
-TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
- const size_t kNumThreads = 1u;
- scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
- scoped_ptr<MultiThreadedProxyResolver> resolver(
- new MultiThreadedProxyResolver(
- new ForwardingProxyResolverFactory(mock.get()), kNumThreads));
+TEST(SingleThreadedProxyResolverTest, CancelRequestByDeleting) {
+ BlockableProxyResolver* mock = new BlockableProxyResolver;
+ scoped_ptr<SingleThreadedProxyResolver> resolver(
+ new SingleThreadedProxyResolver(mock));
int rv;
- // Initialize the resolver.
- TestCompletionCallback init_callback;
- rv = resolver->SetPacScriptByData(ASCIIToUTF16("foo"), &init_callback);
- EXPECT_EQ(OK, init_callback.WaitForResult());
-
// Block the proxy resolver, so no request can complete.
mock->Block();
@@ -509,14 +397,14 @@ TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
mock->WaitUntilBlocked();
// Add some latency, to improve the chance that when
- // MultiThreadedProxyResolver is deleted below we are still running inside
+ // SingleThreadedProxyResolver is deleted below we are still running inside
// of the worker thread. The test will pass regardless, so this race doesn't
// cause flakiness. However the destruction during execution is a more
// interesting case to test.
mock->SetResolveLatency(100);
// Unblock the worker thread and delete the underlying
- // MultiThreadedProxyResolver immediately.
+ // SingleThreadedProxyResolver immediately.
mock->Unblock();
resolver.reset();
@@ -530,213 +418,59 @@ TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
}
// Cancel an outstanding call to SetPacScriptByData().
-TEST(MultiThreadedProxyResolverTest, SingleThread_CancelSetPacScript) {
- const size_t kNumThreads = 1u;
- scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
- MultiThreadedProxyResolver resolver(
- new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
-
- int rv;
-
- TestCompletionCallback set_pac_script_callback;
- rv = resolver.SetPacScriptByData(ASCIIToUTF16("data"),
- &set_pac_script_callback);
- EXPECT_EQ(ERR_IO_PENDING, rv);
-
- // Cancel the SetPacScriptByData request.
- resolver.CancelSetPacScript();
-
- // Start another SetPacScript request
- TestCompletionCallback set_pac_script_callback2;
- rv = resolver.SetPacScriptByData(ASCIIToUTF16("data2"),
- &set_pac_script_callback2);
- EXPECT_EQ(ERR_IO_PENDING, rv);
-
- // Wait for the initialization to complete.
-
- rv = set_pac_script_callback2.WaitForResult();
- EXPECT_EQ(0, rv);
- EXPECT_EQ(ASCIIToUTF16("data2"), mock->last_pac_script());
-
- // The first SetPacScript callback should never have been completed.
- EXPECT_FALSE(set_pac_script_callback.have_result());
-}
-
-// Tests setting the PAC script once, lazily creating new threads, and
-// cancelling requests.
-TEST(MultiThreadedProxyResolverTest, ThreeThreads_Basic) {
- const size_t kNumThreads = 3u;
- BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
- MultiThreadedProxyResolver resolver(factory, kNumThreads);
+TEST(SingleThreadedProxyResolverTest, CancelSetPacScript) {
+ BlockableProxyResolver* mock = new BlockableProxyResolver;
+ SingleThreadedProxyResolver resolver(mock);
int rv;
- EXPECT_TRUE(resolver.expects_pac_bytes());
+ // Block the proxy resolver, so no request can complete.
+ mock->Block();
- // Call SetPacScriptByData() -- verify that it reaches the synchronous
- // resolver.
- TestCompletionCallback set_script_callback;
- rv = resolver.SetPacScriptByData(ASCIIToUTF16("pac script bytes"),
- &set_script_callback);
- EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(OK, set_script_callback.WaitForResult());
- // One thread has been provisioned (i.e. one ProxyResolver was created).
- ASSERT_EQ(1u, factory->resolvers().size());
- EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
- factory->resolvers()[0]->last_pac_script());
-
- const int kNumRequests = 9;
- TestCompletionCallback callback[kNumRequests];
- ProxyInfo results[kNumRequests];
- ProxyResolver::RequestHandle request[kNumRequests];
-
- // Start request 0 -- this should run on thread 0 as there is nothing else
- // going on right now.
+ // Start request 0.
+ ProxyResolver::RequestHandle request0;
+ TestCompletionCallback callback0;
+ ProxyInfo results0;
rv = resolver.GetProxyForURL(
- GURL("http://request0"), &results[0], &callback[0], &request[0],
- BoundNetLog());
- EXPECT_EQ(ERR_IO_PENDING, rv);
-
- // Wait for request 0 to finish.
- rv = callback[0].WaitForResult();
- EXPECT_EQ(0, rv);
- EXPECT_EQ("PROXY request0:80", results[0].ToPacString());
- ASSERT_EQ(1u, factory->resolvers().size());
- EXPECT_EQ(1, factory->resolvers()[0]->request_count());
-
- MessageLoop::current()->RunAllPending();
-
- // We now start 8 requests in parallel -- this will cause the maximum of
- // three threads to be provisioned (an additional two from what we already
- // have).
-
- for (int i = 1; i < kNumRequests; ++i) {
- rv = resolver.GetProxyForURL(
- GURL(StringPrintf("http://request%d", i)), &results[i], &callback[i],
- &request[i], BoundNetLog());
- EXPECT_EQ(ERR_IO_PENDING, rv);
- }
-
- // We should now have a total of 3 threads, each with its own ProxyResolver
- // that will get initialized with the same data. (We check this later since
- // the assignment happens on the worker threads and may not have occurred
- // yet.)
- ASSERT_EQ(3u, factory->resolvers().size());
-
- // Cancel 3 of the 8 oustanding requests.
- resolver.CancelRequest(request[1]);
- resolver.CancelRequest(request[3]);
- resolver.CancelRequest(request[6]);
-
- // Wait for the remaining requests to complete.
- int kNonCancelledRequests[] = {2, 4, 5, 7, 8};
- for (size_t i = 0; i < arraysize(kNonCancelledRequests); ++i) {
- int request_index = kNonCancelledRequests[i];
- EXPECT_GE(callback[request_index].WaitForResult(), 0);
- }
-
- // Check that the cancelled requests never invoked their callback.
- EXPECT_FALSE(callback[1].have_result());
- EXPECT_FALSE(callback[3].have_result());
- EXPECT_FALSE(callback[6].have_result());
-
- // We call SetPacScript again, solely to stop the current worker threads.
- // (That way we can test to see the values observed by the synchronous
- // resolvers in a non-racy manner).
- TestCompletionCallback set_script_callback2;
- rv = resolver.SetPacScriptByData(ASCIIToUTF16("xyz"), &set_script_callback2);
+ GURL("http://request0"), &results0, &callback0, &request0, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(OK, set_script_callback2.WaitForResult());
- ASSERT_EQ(4u, factory->resolvers().size());
-
- for (int i = 0; i < 3; ++i) {
- EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
- factory->resolvers()[i]->last_pac_script()) << "i=" << i;
- }
-
- EXPECT_EQ(ASCIIToUTF16("xyz"),
- factory->resolvers()[3]->last_pac_script());
-
- // We don't know the exact ordering that requests ran on threads with,
- // but we do know the total count that should have reached the threads.
- // 8 total were submitted, and three were cancelled. Of the three that
- // were cancelled, one of them (request 1) was cancelled after it had
- // already been posted to the worker thread. So the resolvers will
- // have seen 6 total (and 1 from the run prior).
- ASSERT_EQ(4u, factory->resolvers().size());
- int total_count = 0;
- for (int i = 0; i < 3; ++i) {
- total_count += factory->resolvers()[i]->request_count();
- }
- EXPECT_EQ(7, total_count);
-}
-
-// Tests using two threads. The first request hangs the first thread. Checks
-// that other requests are able to complete while this first request remains
-// stalled.
-TEST(MultiThreadedProxyResolverTest, OneThreadBlocked) {
- const size_t kNumThreads = 2u;
- BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
- MultiThreadedProxyResolver resolver(factory, kNumThreads);
-
- int rv;
- EXPECT_TRUE(resolver.expects_pac_bytes());
+ // Wait until requests 0 reaches the worker thread.
+ mock->WaitUntilBlocked();
- // Initialize the resolver.
- TestCompletionCallback set_script_callback;
- rv = resolver.SetPacScriptByData(ASCIIToUTF16("pac script bytes"),
- &set_script_callback);
+ TestCompletionCallback set_pac_script_callback;
+ rv = resolver.SetPacScriptByData(ASCIIToUTF16("data"),
+ &set_pac_script_callback);
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(OK, set_script_callback.WaitForResult());
- // One thread has been provisioned (i.e. one ProxyResolver was created).
- ASSERT_EQ(1u, factory->resolvers().size());
- EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
- factory->resolvers()[0]->last_pac_script());
- const int kNumRequests = 4;
- TestCompletionCallback callback[kNumRequests];
- ProxyInfo results[kNumRequests];
- ProxyResolver::RequestHandle request[kNumRequests];
-
- // Start a request that will block the first thread.
+ // Cancel the SetPacScriptByData request (it can't have finished yet,
+ // since the single-thread is currently blocked).
+ resolver.CancelSetPacScript();
- factory->resolvers()[0]->Block();
+ // Start 1 more request.
+ TestCompletionCallback callback1;
+ ProxyInfo results1;
rv = resolver.GetProxyForURL(
- GURL("http://request0"), &results[0], &callback[0], &request[0],
- BoundNetLog());
-
+ GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
- factory->resolvers()[0]->WaitUntilBlocked();
- // Start 3 more requests -- they should all be serviced by thread #2
- // since thread #1 is blocked.
+ // Unblock the worker thread so the requests can continue running.
+ mock->Unblock();
- for (int i = 1; i < kNumRequests; ++i) {
- rv = resolver.GetProxyForURL(
- GURL(StringPrintf("http://request%d", i)),
- &results[i], &callback[i], &request[i], BoundNetLog());
- EXPECT_EQ(ERR_IO_PENDING, rv);
- }
+ // Wait for requests 0 and 1 to finish.
- // Wait for the three requests to complete (they should complete in FIFO
- // order).
- for (int i = 1; i < kNumRequests; ++i) {
- EXPECT_EQ(i - 1, callback[i].WaitForResult());
- }
+ rv = callback0.WaitForResult();
+ EXPECT_EQ(0, rv);
+ EXPECT_EQ("PROXY request0:80", results0.ToPacString());
- // Unblock the first thread.
- factory->resolvers()[0]->Unblock();
- EXPECT_EQ(0, callback[0].WaitForResult());
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(1, rv);
+ EXPECT_EQ("PROXY request1:80", results1.ToPacString());
- // All in all, the first thread should have seen just 1 request. And the
- // second thread 3 requests.
- ASSERT_EQ(2u, factory->resolvers().size());
- EXPECT_EQ(1, factory->resolvers()[0]->request_count());
- EXPECT_EQ(3, factory->resolvers()[1]->request_count());
+ // The SetPacScript callback should never have been completed.
+ EXPECT_FALSE(set_pac_script_callback.have_result());
}
} // namespace
-
} // namespace net
diff --git a/net/proxy/sync_host_resolver_bridge.cc b/net/proxy/sync_host_resolver_bridge.cc
index 6c62c7d..caf4549 100644
--- a/net/proxy/sync_host_resolver_bridge.cc
+++ b/net/proxy/sync_host_resolver_bridge.cc
@@ -188,4 +188,19 @@ void SyncHostResolverBridge::Shutdown() {
core_->Shutdown();
}
+// SingleThreadedProxyResolverUsingBridgedHostResolver -----------------------
+
+SingleThreadedProxyResolverUsingBridgedHostResolver::
+SingleThreadedProxyResolverUsingBridgedHostResolver(
+ ProxyResolver* proxy_resolver,
+ SyncHostResolverBridge* bridged_host_resolver)
+ : SingleThreadedProxyResolver(proxy_resolver),
+ bridged_host_resolver_(bridged_host_resolver) {
+}
+
+SingleThreadedProxyResolverUsingBridgedHostResolver::
+~SingleThreadedProxyResolverUsingBridgedHostResolver() {
+ bridged_host_resolver_->Shutdown();
+}
+
} // namespace net
diff --git a/net/proxy/sync_host_resolver_bridge.h b/net/proxy/sync_host_resolver_bridge.h
index b02d496..4d25b4d 100644
--- a/net/proxy/sync_host_resolver_bridge.h
+++ b/net/proxy/sync_host_resolver_bridge.h
@@ -7,6 +7,7 @@
#include "base/scoped_ptr.h"
#include "net/base/host_resolver.h"
+#include "net/proxy/single_threaded_proxy_resolver.h"
class MessageLoop;
@@ -34,7 +35,7 @@ class SyncHostResolverBridge : public HostResolver {
// The Shutdown() method should be called prior to destruction, from
// |host_resolver_loop_|. It aborts any in progress synchronous resolves, to
// prevent deadlocks from happening.
- virtual void Shutdown();
+ void Shutdown();
private:
class Core;
@@ -43,6 +44,22 @@ class SyncHostResolverBridge : public HostResolver {
scoped_refptr<Core> core_;
};
+// Subclass of SingleThreadedProxyResolver that additionally calls
+// |bridged_host_resolver_->Shutdown()| during its destructor.
+class SingleThreadedProxyResolverUsingBridgedHostResolver
+ : public SingleThreadedProxyResolver {
+ public:
+ SingleThreadedProxyResolverUsingBridgedHostResolver(
+ ProxyResolver* proxy_resolver,
+ SyncHostResolverBridge* bridged_host_resolver);
+
+ virtual ~SingleThreadedProxyResolverUsingBridgedHostResolver();
+
+ private:
+ scoped_refptr<SyncHostResolverBridge> bridged_host_resolver_;
+};
+
+
} // namespace net
#endif // NET_PROXY_SYNC_HOST_RESOLVER_BRIDGE_H_
diff --git a/net/proxy/sync_host_resolver_bridge_unittest.cc b/net/proxy/sync_host_resolver_bridge_unittest.cc
index 5f02213..62c662a 100644
--- a/net/proxy/sync_host_resolver_bridge_unittest.cc
+++ b/net/proxy/sync_host_resolver_bridge_unittest.cc
@@ -9,14 +9,10 @@
#include "net/base/address_list.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
-#include "net/proxy/multi_threaded_proxy_resolver.h"
#include "net/base/test_completion_callback.h"
#include "net/proxy/proxy_info.h"
#include "testing/gtest/include/gtest/gtest.h"
-// TODO(eroman): This test should be moved into
-// multi_threaded_proxy_resolver_unittest.cc.
-
namespace net {
namespace {
@@ -79,7 +75,7 @@ class BlockableHostResolver : public HostResolver {
// on |host_resolver| in response to GetProxyForURL().
class SyncProxyResolver : public ProxyResolver {
public:
- explicit SyncProxyResolver(SyncHostResolverBridge* host_resolver)
+ explicit SyncProxyResolver(HostResolver* host_resolver)
: ProxyResolver(false), host_resolver_(host_resolver) {}
virtual int GetProxyForURL(const GURL& url,
@@ -105,33 +101,15 @@ class SyncProxyResolver : public ProxyResolver {
NOTREACHED();
}
- virtual void Shutdown() {
- host_resolver_->Shutdown();
- }
-
private:
virtual int SetPacScript(const GURL& pac_url,
const string16& pac_script,
CompletionCallback* callback) {
+ NOTREACHED();
return OK;
}
- scoped_refptr<SyncHostResolverBridge> host_resolver_;
-};
-
-class SyncProxyResolverFactory : public ProxyResolverFactory {
- public:
- explicit SyncProxyResolverFactory(SyncHostResolverBridge* sync_host_resolver)
- : ProxyResolverFactory(false),
- sync_host_resolver_(sync_host_resolver) {
- }
-
- virtual ProxyResolver* CreateProxyResolver() {
- return new SyncProxyResolver(sync_host_resolver_);
- }
-
- private:
- scoped_refptr<SyncHostResolverBridge> sync_host_resolver_;
+ scoped_refptr<HostResolver> host_resolver_;
};
// This helper thread is used to create the circumstances for the deadlock.
@@ -159,14 +137,9 @@ class IOThread : public base::Thread {
new SyncHostResolverBridge(async_resolver_, message_loop());
proxy_resolver_.reset(
- new MultiThreadedProxyResolver(
- new SyncProxyResolverFactory(sync_resolver),
- 1u));
-
- // Initialize the resolver.
- TestCompletionCallback callback;
- proxy_resolver_->SetPacScriptByUrl(GURL(), &callback);
- EXPECT_EQ(OK, callback.WaitForResult());
+ new SingleThreadedProxyResolverUsingBridgedHostResolver(
+ new SyncProxyResolver(sync_resolver),
+ sync_resolver));
// Start an asynchronous request to the proxy resolver
// (note that it will never complete).
@@ -205,7 +178,7 @@ class IOThread : public base::Thread {
// Test that a deadlock does not happen during shutdown when a host resolve
// is outstanding on the SyncHostResolverBridge.
// This is a regression test for http://crbug.com/41244.
-TEST(MultiThreadedProxyResolverTest, ShutdownIsCalledBeforeThreadJoin) {
+TEST(SingleThreadedProxyResolverWithBridgedHostResolverTest, ShutdownDeadlock) {
IOThread io_thread;
base::Thread::Options options;
options.message_loop_type = MessageLoop::TYPE_IO;