1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/proxy/sync_host_resolver_bridge.h"
6
7 #include "base/threading/thread.h"
8 #include "base/synchronization/waitable_event.h"
9 #include "net/base/address_list.h"
10 #include "net/base/net_errors.h"
11 #include "net/base/net_log.h"
12 #include "net/proxy/multi_threaded_proxy_resolver.h"
13 #include "net/base/test_completion_callback.h"
14 #include "net/proxy/proxy_info.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 // TODO(eroman): This test should be moved into
18 // multi_threaded_proxy_resolver_unittest.cc.
19
20 namespace net {
21
22 namespace {
23
24 // This implementation of HostResolver allows blocking until a resolve request
25 // has been received. The resolve requests it receives will never be completed.
26 class BlockableHostResolver : public HostResolver {
27 public:
BlockableHostResolver()28 BlockableHostResolver()
29 : event_(true, false),
30 was_request_cancelled_(false) {
31 }
32
Resolve(const RequestInfo & info,AddressList * addresses,CompletionCallback * callback,RequestHandle * out_req,const BoundNetLog & net_log)33 virtual int Resolve(const RequestInfo& info,
34 AddressList* addresses,
35 CompletionCallback* callback,
36 RequestHandle* out_req,
37 const BoundNetLog& net_log) {
38 EXPECT_TRUE(callback);
39 EXPECT_TRUE(out_req);
40 *out_req = reinterpret_cast<RequestHandle*>(1); // Magic value.
41
42 // Indicate to the caller that a request was received.
43 event_.Signal();
44
45 // We return ERR_IO_PENDING, as this request will NEVER be completed.
46 // Expectation is for the caller to later cancel the request.
47 return ERR_IO_PENDING;
48 }
49
CancelRequest(RequestHandle req)50 virtual void CancelRequest(RequestHandle req) {
51 EXPECT_EQ(reinterpret_cast<RequestHandle*>(1), req);
52 was_request_cancelled_ = true;
53 }
54
AddObserver(Observer * observer)55 virtual void AddObserver(Observer* observer) {
56 NOTREACHED();
57 }
58
RemoveObserver(Observer * observer)59 virtual void RemoveObserver(Observer* observer) {
60 NOTREACHED();
61 }
62
63 // Waits until Resolve() has been called.
WaitUntilRequestIsReceived()64 void WaitUntilRequestIsReceived() {
65 event_.Wait();
66 }
67
was_request_cancelled() const68 bool was_request_cancelled() const {
69 return was_request_cancelled_;
70 }
71
72 private:
73 // Event to notify when a resolve request was received.
74 base::WaitableEvent event_;
75 bool was_request_cancelled_;
76 };
77
78 // This implementation of ProxyResolver simply does a synchronous resolve
79 // on |host_resolver| in response to GetProxyForURL().
80 class SyncProxyResolver : public ProxyResolver {
81 public:
SyncProxyResolver(SyncHostResolverBridge * host_resolver)82 explicit SyncProxyResolver(SyncHostResolverBridge* host_resolver)
83 : ProxyResolver(false), host_resolver_(host_resolver) {}
84
GetProxyForURL(const GURL & url,ProxyInfo * results,CompletionCallback * callback,RequestHandle * request,const BoundNetLog & net_log)85 virtual int GetProxyForURL(const GURL& url,
86 ProxyInfo* results,
87 CompletionCallback* callback,
88 RequestHandle* request,
89 const BoundNetLog& net_log) {
90 EXPECT_FALSE(callback);
91 EXPECT_FALSE(request);
92
93 // Do a synchronous host resolve.
94 HostResolver::RequestInfo info(HostPortPair::FromURL(url));
95 AddressList addresses;
96 int rv =
97 host_resolver_->Resolve(info, &addresses, NULL, NULL, BoundNetLog());
98
99 EXPECT_EQ(ERR_ABORTED, rv);
100
101 return rv;
102 }
103
CancelRequest(RequestHandle request)104 virtual void CancelRequest(RequestHandle request) {
105 NOTREACHED();
106 }
107
Shutdown()108 virtual void Shutdown() {
109 host_resolver_->Shutdown();
110 }
111
CancelSetPacScript()112 virtual void CancelSetPacScript() {
113 NOTREACHED();
114 }
115
SetPacScript(const scoped_refptr<ProxyResolverScriptData> & script_data,CompletionCallback * callback)116 virtual int SetPacScript(
117 const scoped_refptr<ProxyResolverScriptData>& script_data,
118 CompletionCallback* callback) {
119 return OK;
120 }
121
122 private:
123 SyncHostResolverBridge* const host_resolver_;
124 };
125
126 class SyncProxyResolverFactory : public ProxyResolverFactory {
127 public:
128 // Takes ownership of |sync_host_resolver|.
SyncProxyResolverFactory(SyncHostResolverBridge * sync_host_resolver)129 explicit SyncProxyResolverFactory(SyncHostResolverBridge* sync_host_resolver)
130 : ProxyResolverFactory(false),
131 sync_host_resolver_(sync_host_resolver) {
132 }
133
CreateProxyResolver()134 virtual ProxyResolver* CreateProxyResolver() {
135 return new SyncProxyResolver(sync_host_resolver_.get());
136 }
137
138 private:
139 const scoped_ptr<SyncHostResolverBridge> sync_host_resolver_;
140 };
141
142 // This helper thread is used to create the circumstances for the deadlock.
143 // It is analagous to the "IO thread" which would be main thread running the
144 // network stack.
145 class IOThread : public base::Thread {
146 public:
IOThread()147 IOThread() : base::Thread("IO-thread") {}
148
~IOThread()149 virtual ~IOThread() {
150 Stop();
151 }
152
async_resolver()153 BlockableHostResolver* async_resolver() {
154 return async_resolver_.get();
155 }
156
157 protected:
Init()158 virtual void Init() {
159 async_resolver_.reset(new BlockableHostResolver());
160
161 // Create a synchronous host resolver that operates the async host
162 // resolver on THIS thread.
163 SyncHostResolverBridge* sync_resolver =
164 new SyncHostResolverBridge(async_resolver_.get(), message_loop());
165
166 proxy_resolver_.reset(
167 new MultiThreadedProxyResolver(
168 new SyncProxyResolverFactory(sync_resolver),
169 1u));
170
171 // Initialize the resolver.
172 TestCompletionCallback callback;
173 proxy_resolver_->SetPacScript(ProxyResolverScriptData::FromURL(GURL()),
174 &callback);
175 EXPECT_EQ(OK, callback.WaitForResult());
176
177 // Start an asynchronous request to the proxy resolver
178 // (note that it will never complete).
179 proxy_resolver_->GetProxyForURL(GURL("http://test/"), &results_,
180 &callback_, &request_, BoundNetLog());
181 }
182
CleanUp()183 virtual void CleanUp() {
184 // Cancel the outstanding request (note however that this will not
185 // unblock the PAC thread though).
186 proxy_resolver_->CancelRequest(request_);
187
188 // Delete the single threaded proxy resolver.
189 proxy_resolver_.reset();
190
191 // (There may have been a completion posted back to origin thread, avoid
192 // leaking it by running).
193 MessageLoop::current()->RunAllPending();
194
195 // During the teardown sequence of the single threaded proxy resolver,
196 // the outstanding host resolve should have been cancelled.
197 EXPECT_TRUE(async_resolver_->was_request_cancelled());
198 }
199
200 private:
201 // This (async) host resolver will outlive the thread that is operating it
202 // synchronously.
203 scoped_ptr<BlockableHostResolver> async_resolver_;
204
205 scoped_ptr<ProxyResolver> proxy_resolver_;
206
207 // Data for the outstanding request to the single threaded proxy resolver.
208 TestCompletionCallback callback_;
209 ProxyInfo results_;
210 ProxyResolver::RequestHandle request_;
211 };
212
213 // Test that a deadlock does not happen during shutdown when a host resolve
214 // is outstanding on the SyncHostResolverBridge.
215 // This is a regression test for http://crbug.com/41244.
TEST(MultiThreadedProxyResolverTest,ShutdownIsCalledBeforeThreadJoin)216 TEST(MultiThreadedProxyResolverTest, ShutdownIsCalledBeforeThreadJoin) {
217 IOThread io_thread;
218 base::Thread::Options options;
219 options.message_loop_type = MessageLoop::TYPE_IO;
220 ASSERT_TRUE(io_thread.StartWithOptions(options));
221
222 io_thread.async_resolver()->WaitUntilRequestIsReceived();
223
224 // Now upon exitting this scope, the IOThread is destroyed -- this will
225 // stop the IOThread, which will in turn delete the
226 // SingleThreadedProxyResolver, which in turn will stop its internal
227 // PAC thread (which is currently blocked waiting on the host resolve which
228 // is running on IOThread). The IOThread::Cleanup() will verify that after
229 // the PAC thread is stopped, it cancels the request on the HostResolver.
230 }
231
232 } // namespace
233
234 } // namespace net
235