summaryrefslogtreecommitdiffstats
path: root/net/proxy
diff options
context:
space:
mode:
authoreroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-17 00:07:04 +0000
committereroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-17 00:07:04 +0000
commit1ca2b45a6e0b6490d0ab1ff940e2d7769660682b (patch)
treef6740d35ae7bf87007f6794a49f53f2858ead7b7 /net/proxy
parent54ce726e1af62939cb12b09dc687737b114d426c (diff)
downloadchromium_src-1ca2b45a6e0b6490d0ab1ff940e2d7769660682b.zip
chromium_src-1ca2b45a6e0b6490d0ab1ff940e2d7769660682b.tar.gz
chromium_src-1ca2b45a6e0b6490d0ab1ff940e2d7769660682b.tar.bz2
Fix a deadlock that could happen during shutdown if a host resolve request was outstanding by a PAC script.
The solution is to abort the oustanding host resolver request during shutdown, and wake-up the blocked PAC thread. BUG=41244 TEST=SingleThreadedProxyResolverWithBridgedHostResolverTest.ShutdownDeadlock Review URL: http://codereview.chromium.org/1527037 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@44864 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/proxy')
-rw-r--r--net/proxy/proxy_resolver_js_bindings.cc100
-rw-r--r--net/proxy/proxy_resolver_js_bindings.h7
-rw-r--r--net/proxy/proxy_resolver_js_bindings_unittest.cc10
-rw-r--r--net/proxy/proxy_resolver_perftest.cc3
-rw-r--r--net/proxy/proxy_service.cc30
-rw-r--r--net/proxy/single_threaded_proxy_resolver.cc15
-rw-r--r--net/proxy/sync_host_resolver_bridge.cc123
-rw-r--r--net/proxy/sync_host_resolver_bridge.h97
-rw-r--r--net/proxy/sync_host_resolver_bridge_unittest.cc199
9 files changed, 468 insertions, 116 deletions
diff --git a/net/proxy/proxy_resolver_js_bindings.cc b/net/proxy/proxy_resolver_js_bindings.cc
index 0906e7a..4703f2e 100644
--- a/net/proxy/proxy_resolver_js_bindings.cc
+++ b/net/proxy/proxy_resolver_js_bindings.cc
@@ -4,10 +4,7 @@
#include "net/proxy/proxy_resolver_js_bindings.h"
-#include "base/compiler_specific.h"
#include "base/logging.h"
-#include "base/message_loop.h"
-#include "base/waitable_event.h"
#include "net/base/address_list.h"
#include "net/base/host_resolver.h"
#include "net/base/net_errors.h"
@@ -18,85 +15,11 @@
namespace net {
namespace {
-// Wrapper around HostResolver to give a sync API while running the resolve
-// in async mode on |host_resolver_loop|. If |host_resolver_loop| is NULL,
-// runs sync on the current thread (this mode is just used by testing).
-class SyncHostResolverBridge
- : public base::RefCountedThreadSafe<SyncHostResolverBridge> {
- public:
- SyncHostResolverBridge(HostResolver* host_resolver,
- MessageLoop* host_resolver_loop)
- : host_resolver_(host_resolver),
- host_resolver_loop_(host_resolver_loop),
- event_(false, false),
- ALLOW_THIS_IN_INITIALIZER_LIST(
- callback_(this, &SyncHostResolverBridge::OnResolveCompletion)) {
- }
-
- // Run the resolve on host_resolver_loop, and wait for result.
- int Resolve(const std::string& hostname,
- AddressFamily address_family,
- net::AddressList* addresses) {
- // Port number doesn't matter.
- HostResolver::RequestInfo info(hostname, 80);
- info.set_address_family(address_family);
-
- // Hack for tests -- run synchronously on current thread.
- if (!host_resolver_loop_)
- return host_resolver_->Resolve(info, addresses, NULL, NULL,
- BoundNetLog());
-
- // Otherwise start an async resolve on the resolver's thread.
- host_resolver_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
- &SyncHostResolverBridge::StartResolve, info, addresses));
-
- // Wait for the resolve to complete in the resolver's thread.
- event_.Wait();
- return err_;
- }
-
- private:
- friend class base::RefCountedThreadSafe<SyncHostResolverBridge>;
-
- ~SyncHostResolverBridge() {}
-
- // Called on host_resolver_loop_.
- void StartResolve(const HostResolver::RequestInfo& info,
- net::AddressList* addresses) {
- DCHECK_EQ(host_resolver_loop_, MessageLoop::current());
- int error = host_resolver_->Resolve(
- info, addresses, &callback_, NULL, BoundNetLog());
- if (error != ERR_IO_PENDING)
- OnResolveCompletion(error); // Completed synchronously.
- }
-
- // Called on host_resolver_loop_.
- void OnResolveCompletion(int result) {
- DCHECK_EQ(host_resolver_loop_, MessageLoop::current());
- err_ = result;
- event_.Signal();
- }
-
- scoped_refptr<HostResolver> host_resolver_;
- MessageLoop* host_resolver_loop_;
-
- // Event to notify completion of resolve request.
- base::WaitableEvent event_;
-
- // Callback for when the resolve completes on host_resolver_loop_.
- net::CompletionCallbackImpl<SyncHostResolverBridge> callback_;
-
- // The result from the result request (set by in host_resolver_loop_).
- int err_;
-};
-
// ProxyResolverJSBindings implementation.
class DefaultJSBindings : public ProxyResolverJSBindings {
public:
- DefaultJSBindings(HostResolver* host_resolver,
- MessageLoop* host_resolver_loop)
- : host_resolver_(new SyncHostResolverBridge(
- host_resolver, host_resolver_loop)) {}
+ explicit DefaultJSBindings(HostResolver* host_resolver)
+ : host_resolver_(host_resolver) {}
// Handler for "alert(message)".
virtual void Alert(const std::string& message) {
@@ -124,10 +47,11 @@ class DefaultJSBindings : public ProxyResolverJSBindings {
// Consequently a lot of existing PAC scripts assume they will only get
// IPv4 results, and will misbehave if they get an IPv6 result.
// See http://crbug.com/24641 for more details.
+ HostResolver::RequestInfo info(host, 80); // Port doesn't matter.
+ info.set_address_family(ADDRESS_FAMILY_IPV4);
net::AddressList address_list;
- int result = host_resolver_->Resolve(host,
- ADDRESS_FAMILY_IPV4,
- &address_list);
+ int result = host_resolver_->Resolve(info, &address_list, NULL, NULL,
+ BoundNetLog());
if (result != OK)
return std::string(); // Failed.
@@ -143,10 +67,10 @@ class DefaultJSBindings : public ProxyResolverJSBindings {
// Handler for "dnsResolveEx(host)". Returns empty string on failure.
virtual std::string DnsResolveEx(const std::string& host) {
// Do a sync resolve of the hostname.
+ HostResolver::RequestInfo info(host, 80); // Port doesn't matter.
net::AddressList address_list;
- int result = host_resolver_->Resolve(host,
- ADDRESS_FAMILY_UNSPECIFIED,
- &address_list);
+ int result = host_resolver_->Resolve(info, &address_list, NULL, NULL,
+ BoundNetLog());
if (result != OK)
return std::string(); // Failed.
@@ -174,15 +98,15 @@ class DefaultJSBindings : public ProxyResolverJSBindings {
}
private:
- scoped_refptr<SyncHostResolverBridge> host_resolver_;
+ scoped_refptr<HostResolver> host_resolver_;
};
} // namespace
// static
ProxyResolverJSBindings* ProxyResolverJSBindings::CreateDefault(
- HostResolver* host_resolver, MessageLoop* host_resolver_loop) {
- return new DefaultJSBindings(host_resolver, host_resolver_loop);
+ HostResolver* host_resolver) {
+ return new DefaultJSBindings(host_resolver);
}
} // namespace net
diff --git a/net/proxy/proxy_resolver_js_bindings.h b/net/proxy/proxy_resolver_js_bindings.h
index 03ad61a..09e6e1c 100644
--- a/net/proxy/proxy_resolver_js_bindings.h
+++ b/net/proxy/proxy_resolver_js_bindings.h
@@ -48,11 +48,8 @@ class ProxyResolverJSBindings {
// - Send script alert()s to LOG(INFO)
// - Use the provided host resolver to service dnsResolve().
//
- // |host_resolver| will be used in async mode on |host_resolver_loop|. If
- // |host_resolver_loop| is NULL, then |host_resolver| will be used in sync
- // mode on the PAC thread.
- static ProxyResolverJSBindings* CreateDefault(
- HostResolver* host_resolver, MessageLoop* host_resolver_loop);
+ // Note that |host_resolver| will be used in sync mode mode.
+ static ProxyResolverJSBindings* CreateDefault(HostResolver* host_resolver);
};
} // namespace net
diff --git a/net/proxy/proxy_resolver_js_bindings_unittest.cc b/net/proxy/proxy_resolver_js_bindings_unittest.cc
index 2d525f7..5bf9c3d 100644
--- a/net/proxy/proxy_resolver_js_bindings_unittest.cc
+++ b/net/proxy/proxy_resolver_js_bindings_unittest.cc
@@ -83,7 +83,7 @@ TEST(ProxyResolverJSBindingsTest, DnsResolve) {
// Get a hold of a DefaultJSBindings* (it is a hidden impl class).
scoped_ptr<ProxyResolverJSBindings> bindings(
- ProxyResolverJSBindings::CreateDefault(host_resolver, NULL));
+ ProxyResolverJSBindings::CreateDefault(host_resolver));
// Empty string is not considered a valid host (even though on some systems
// requesting this will resolve to localhost).
@@ -105,8 +105,7 @@ TEST(ProxyResolverJSBindingsTest, DnsResolve) {
TEST(ProxyResolverJSBindingsTest, MyIpAddress) {
// Get a hold of a DefaultJSBindings* (it is a hidden impl class).
scoped_ptr<ProxyResolverJSBindings> bindings(
- ProxyResolverJSBindings::CreateDefault(
- new MockHostResolver, NULL));
+ ProxyResolverJSBindings::CreateDefault(new MockHostResolver));
// Our IP address is always going to be 127.0.0.1, since we are using a
// mock host resolver.
@@ -132,8 +131,7 @@ TEST(ProxyResolverJSBindingsTest, RestrictAddressFamily) {
// Get a hold of a DefaultJSBindings* (it is a hidden impl class).
scoped_ptr<ProxyResolverJSBindings> bindings(
- ProxyResolverJSBindings::CreateDefault(
- host_resolver, NULL));
+ ProxyResolverJSBindings::CreateDefault(host_resolver));
// Make it so requests resolve to particular address patterns based on family:
// IPV4_ONLY --> 192.168.1.*
@@ -179,7 +177,7 @@ TEST(ProxyResolverJSBindingsTest, ExFunctionsReturnList) {
// Get a hold of a DefaultJSBindings* (it is a hidden impl class).
scoped_ptr<ProxyResolverJSBindings> bindings(
- ProxyResolverJSBindings::CreateDefault(host_resolver, NULL));
+ ProxyResolverJSBindings::CreateDefault(host_resolver));
EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2",
bindings->MyIpAddressEx());
diff --git a/net/proxy/proxy_resolver_perftest.cc b/net/proxy/proxy_resolver_perftest.cc
index 6e336e9..7650be4 100644
--- a/net/proxy/proxy_resolver_perftest.cc
+++ b/net/proxy/proxy_resolver_perftest.cc
@@ -189,8 +189,7 @@ TEST(ProxyResolverPerfTest, ProxyResolverMac) {
TEST(ProxyResolverPerfTest, ProxyResolverV8) {
net::ProxyResolverJSBindings* js_bindings =
- net::ProxyResolverJSBindings::CreateDefault(
- new net::MockHostResolver, NULL);
+ net::ProxyResolverJSBindings::CreateDefault(new net::MockHostResolver);
net::ProxyResolverV8 resolver(js_bindings);
PacPerfSuiteRunner runner(&resolver, "ProxyResolverV8");
diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc
index 668a659..e3778b9 100644
--- a/net/proxy/proxy_service.cc
+++ b/net/proxy/proxy_service.cc
@@ -30,6 +30,7 @@
#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"
using base::TimeDelta;
@@ -222,23 +223,32 @@ ProxyService* ProxyService::Create(
NetworkChangeNotifier* network_change_notifier,
NetLog* net_log,
MessageLoop* io_loop) {
- ProxyResolver* proxy_resolver;
+ ProxyResolver* proxy_resolver = NULL;
if (use_v8_resolver) {
+ // 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).
- HostResolver* host_resolver = url_request_context->host_resolver();
ProxyResolverJSBindings* js_bindings =
- ProxyResolverJSBindings::CreateDefault(host_resolver, io_loop);
-
- proxy_resolver = new ProxyResolverV8(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 {
- proxy_resolver = CreateNonV8ProxyResolver();
+ proxy_resolver =
+ new SingleThreadedProxyResolver(CreateNonV8ProxyResolver());
}
- // Wrap the (synchronous) ProxyResolver implementation in a single-threaded
- // runner. This will dispatch requests to a threadpool of size 1.
- proxy_resolver = new SingleThreadedProxyResolver(proxy_resolver);
-
ProxyService* proxy_service = new ProxyService(
proxy_config_service, proxy_resolver, network_change_notifier,
BoundNetLog::Make(net_log, NetLog::SOURCE_INIT_PROXY_RESOLVER));
diff --git a/net/proxy/single_threaded_proxy_resolver.cc b/net/proxy/single_threaded_proxy_resolver.cc
index 4009e2a..b0fd4a3 100644
--- a/net/proxy/single_threaded_proxy_resolver.cc
+++ b/net/proxy/single_threaded_proxy_resolver.cc
@@ -156,22 +156,23 @@ class SingleThreadedProxyResolver::Job
// Runs on the worker thread.
void DoQuery(ProxyResolver* resolver, size_t load_log_bound) {
- scoped_ptr<CapturingNetLog> worker_log(new CapturingNetLog(load_log_bound));
- BoundNetLog bound_worker_log(NetLog::Source(), worker_log.get());
+ 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, worker_log.release()));
+ NewRunnableMethod(this, &Job::QueryComplete, rv));
}
// Runs the completion callback on the origin thread.
- void QueryComplete(int result_code, CapturingNetLog* worker_log) {
+ 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);
+ 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.
@@ -199,6 +200,10 @@ class SingleThreadedProxyResolver::Job
// 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 ------------------------------------------------
diff --git a/net/proxy/sync_host_resolver_bridge.cc b/net/proxy/sync_host_resolver_bridge.cc
new file mode 100644
index 0000000..31a326d
--- /dev/null
+++ b/net/proxy/sync_host_resolver_bridge.cc
@@ -0,0 +1,123 @@
+// 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/sync_host_resolver_bridge.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+
+namespace net {
+
+// SyncHostResolverBridge -----------------------------------------------------
+
+SyncHostResolverBridge::SyncHostResolverBridge(HostResolver* host_resolver,
+ MessageLoop* host_resolver_loop)
+ : host_resolver_(host_resolver),
+ host_resolver_loop_(host_resolver_loop),
+ event_(true, false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ callback_(this, &SyncHostResolverBridge::OnResolveCompletion)),
+ outstanding_request_(NULL),
+ has_shutdown_(false) {
+ DCHECK(host_resolver_loop_);
+}
+
+SyncHostResolverBridge::~SyncHostResolverBridge() {
+ DCHECK(HasShutdown());
+}
+
+int SyncHostResolverBridge::Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ CompletionCallback* callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) {
+ DCHECK(!callback);
+ DCHECK(!out_req);
+
+ // Otherwise start an async resolve on the resolver's thread.
+ host_resolver_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &SyncHostResolverBridge::StartResolve,
+ info, addresses));
+
+ // Wait for the resolve to complete in the resolver's thread.
+ event_.Wait();
+
+ {
+ AutoLock l(lock_);
+ if (has_shutdown_)
+ return ERR_ABORTED;
+ event_.Reset();
+ }
+
+ return err_;
+}
+
+void SyncHostResolverBridge::CancelRequest(RequestHandle req) {
+ NOTREACHED();
+}
+
+void SyncHostResolverBridge::AddObserver(Observer* observer) {
+ NOTREACHED();
+}
+
+void SyncHostResolverBridge::RemoveObserver(Observer* observer) {
+ NOTREACHED();
+}
+
+void SyncHostResolverBridge::Shutdown() {
+ DCHECK_EQ(MessageLoop::current(), host_resolver_loop_);
+
+ if (outstanding_request_) {
+ host_resolver_->CancelRequest(outstanding_request_);
+ outstanding_request_ = NULL;
+ }
+
+ AutoLock l(lock_);
+ has_shutdown_ = true;
+
+ // Wake up the PAC thread in case it was waiting for resolve completion.
+ event_.Signal();
+}
+
+void SyncHostResolverBridge::StartResolve(const HostResolver::RequestInfo& info,
+ net::AddressList* addresses) {
+ DCHECK_EQ(host_resolver_loop_, MessageLoop::current());
+ DCHECK(!outstanding_request_);
+
+ if (HasShutdown())
+ return;
+
+ int error = host_resolver_->Resolve(
+ info, addresses, &callback_, &outstanding_request_, BoundNetLog());
+ if (error != ERR_IO_PENDING)
+ OnResolveCompletion(error); // Completed synchronously.
+}
+
+void SyncHostResolverBridge::OnResolveCompletion(int result) {
+ DCHECK_EQ(host_resolver_loop_, MessageLoop::current());
+ err_ = result;
+ outstanding_request_ = NULL;
+ event_.Signal();
+}
+
+// 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
new file mode 100644
index 0000000..d082043
--- /dev/null
+++ b/net/proxy/sync_host_resolver_bridge.h
@@ -0,0 +1,97 @@
+// 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.
+
+#ifndef NET_PROXY_SYNC_HOST_RESOLVER_BRIDGE_H_
+#define NET_PROXY_SYNC_HOST_RESOLVER_BRIDGE_H_
+
+#include "base/lock.h"
+#include "base/scoped_ptr.h"
+#include "base/waitable_event.h"
+#include "net/base/host_resolver.h"
+#include "net/proxy/single_threaded_proxy_resolver.h"
+
+class MessageLoop;
+
+namespace net {
+
+// Wrapper around HostResolver to give a sync API while running the resolver
+// in async mode on |host_resolver_loop|.
+class SyncHostResolverBridge : public HostResolver {
+ public:
+ SyncHostResolverBridge(HostResolver* host_resolver,
+ MessageLoop* host_resolver_loop);
+
+ virtual ~SyncHostResolverBridge();
+
+ // HostResolver methods:
+ virtual int Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ CompletionCallback* callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log);
+ virtual void CancelRequest(RequestHandle req);
+ virtual void AddObserver(Observer* observer);
+ virtual void RemoveObserver(Observer* observer);
+
+ // 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.
+ void Shutdown();
+
+ private:
+ // Called on |host_resolver_loop_|.
+ void StartResolve(const HostResolver::RequestInfo& info,
+ net::AddressList* addresses);
+
+ // Called on |host_resolver_loop_|.
+ void OnResolveCompletion(int result);
+
+ // Returns true if Shutdown() has been called.
+ bool HasShutdown() const {
+ AutoLock l(lock_);
+ return has_shutdown_;
+ }
+
+ scoped_refptr<HostResolver> host_resolver_;
+ MessageLoop* host_resolver_loop_;
+
+ // Event to notify completion of resolve request.
+ base::WaitableEvent event_;
+
+ // Callback for when the resolve completes on host_resolver_loop_.
+ net::CompletionCallbackImpl<SyncHostResolverBridge> callback_;
+
+ // The result from the current request (set on |host_resolver_loop_|).
+ int err_;
+
+ // The currently outstanding request to |host_resolver_|, or NULL.
+ HostResolver::RequestHandle outstanding_request_;
+
+ // True if Shutdown() has been called. Must hold |lock_| to access it.
+ bool has_shutdown_;
+
+ // Mutex to guard accesses to |has_shutdown_|.
+ mutable Lock lock_;
+};
+
+// 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
new file mode 100644
index 0000000..916252b
--- /dev/null
+++ b/net/proxy/sync_host_resolver_bridge_unittest.cc
@@ -0,0 +1,199 @@
+// 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/sync_host_resolver_bridge.h"
+
+#include "base/thread.h"
+#include "net/base/address_list.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/test_completion_callback.h"
+#include "net/proxy/proxy_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+// This implementation of HostResolver allows blocking until a resolve request
+// has been received. The resolve requests it receives will never be completed.
+class BlockableHostResolver : public HostResolver {
+ public:
+ BlockableHostResolver()
+ : event_(true, false),
+ was_request_cancelled_(false) {
+ }
+
+ virtual int Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ CompletionCallback* callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) {
+ EXPECT_TRUE(callback);
+ EXPECT_TRUE(out_req);
+ *out_req = reinterpret_cast<RequestHandle*>(1); // Magic value.
+
+ // Indicate to the caller that a request was received.
+ event_.Signal();
+
+ // We return ERR_IO_PENDING, as this request will NEVER be completed.
+ // Expectation is for the caller to later cancel the request.
+ return ERR_IO_PENDING;
+ }
+
+ virtual void CancelRequest(RequestHandle req) {
+ EXPECT_EQ(reinterpret_cast<RequestHandle*>(1), req);
+ was_request_cancelled_ = true;
+ }
+
+ virtual void AddObserver(Observer* observer) {
+ NOTREACHED();
+ }
+
+ virtual void RemoveObserver(Observer* observer) {
+ NOTREACHED();
+ }
+
+ // Waits until Resolve() has been called.
+ void WaitUntilRequestIsReceived() {
+ event_.Wait();
+ }
+
+ bool was_request_cancelled() const {
+ return was_request_cancelled_;
+ }
+
+ private:
+ // Event to notify when a resolve request was received.
+ base::WaitableEvent event_;
+ bool was_request_cancelled_;
+};
+
+// This implementation of ProxyResolver simply does a synchronous resolve
+// on |host_resolver| in response to GetProxyForURL().
+class SyncProxyResolver : public ProxyResolver {
+ public:
+ explicit SyncProxyResolver(HostResolver* host_resolver)
+ : ProxyResolver(false), host_resolver_(host_resolver) {}
+
+ virtual int GetProxyForURL(const GURL& url,
+ ProxyInfo* results,
+ CompletionCallback* callback,
+ RequestHandle* request,
+ const BoundNetLog& net_log) {
+ EXPECT_FALSE(callback);
+ EXPECT_FALSE(request);
+
+ // Do a synchronous host resolve.
+ HostResolver::RequestInfo info(url.host(), 80);
+ AddressList addresses;
+ int rv =
+ host_resolver_->Resolve(info, &addresses, NULL, NULL, BoundNetLog());
+
+ EXPECT_EQ(ERR_ABORTED, rv);
+
+ return rv;
+ }
+
+ virtual void CancelRequest(RequestHandle request) {
+ NOTREACHED();
+ }
+
+ private:
+ virtual int SetPacScript(const GURL& pac_url,
+ const std::string& bytes_utf8,
+ CompletionCallback* callback) {
+ NOTREACHED();
+ return OK;
+ }
+
+ scoped_refptr<HostResolver> host_resolver_;
+};
+
+// This helper thread is used to create the circumstances for the deadlock.
+// It is analagous to the "IO thread" which would be main thread running the
+// network stack.
+class IOThread : public base::Thread {
+ public:
+ explicit IOThread(HostResolver* async_resolver)
+ : base::Thread("IO-thread"), async_resolver_(async_resolver) {
+ }
+
+ virtual ~IOThread() {
+ Stop();
+ }
+
+ protected:
+ virtual void Init() {
+ // Create a synchronous host resolver that operates the async host
+ // resolver on THIS thread.
+ scoped_refptr<SyncHostResolverBridge> sync_resolver =
+ new SyncHostResolverBridge(async_resolver_, message_loop());
+
+ proxy_resolver_.reset(
+ new SingleThreadedProxyResolverUsingBridgedHostResolver(
+ new SyncProxyResolver(sync_resolver),
+ sync_resolver));
+
+ // Start an asynchronous request to the proxy resolver
+ // (note that it will never complete).
+ proxy_resolver_->GetProxyForURL(GURL("http://test/"), &results_,
+ &callback_, &request_, BoundNetLog());
+ }
+
+ virtual void CleanUp() {
+ // Cancel the outstanding request (note however that this will not
+ // unblock the PAC thread though).
+ proxy_resolver_->CancelRequest(request_);
+
+ // Delete the single threaded proxy resolver.
+ proxy_resolver_.reset();
+ }
+
+ private:
+ HostResolver* async_resolver_;
+ scoped_ptr<ProxyResolver> proxy_resolver_;
+
+ // Data for the outstanding request to the single threaded proxy resolver.
+ TestCompletionCallback callback_;
+ ProxyInfo results_;
+ ProxyResolver::RequestHandle request_;
+};
+
+// 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(SingleThreadedProxyResolverWithBridgedHostResolverTest, ShutdownDeadlock) {
+ // This (async) host resolver will outlive the thread that is operating it
+ // synchronously.
+ scoped_refptr<BlockableHostResolver> host_resolver =
+ new BlockableHostResolver();
+
+ {
+ IOThread io_thread(host_resolver.get());
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ ASSERT_TRUE(io_thread.StartWithOptions(options));
+
+ // Wait until the host resolver receives a request (this means that the
+ // PAC thread is now blocked, waiting for the async response from the
+ // host resolver.
+ host_resolver->WaitUntilRequestIsReceived();
+
+ // Now upon exitting this scope, the IOThread is destroyed -- this will
+ // stop the IOThread, which will in turn delete the
+ // SingleThreadedProxyResolver, which in turn will stop its internal
+ // PAC thread (which is currently blocked waiting on the host resolve which
+ // is running on IOThread).
+ }
+
+ // During the teardown sequence of the single threaded proxy resolver,
+ // the outstanding host resolve should have been cancelled.
+ EXPECT_TRUE(host_resolver->was_request_cancelled());
+}
+
+} // namespace
+
+} // namespace net
+