• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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