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
20namespace net {
21
22namespace {
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.
26class BlockableHostResolver : public HostResolver {
27 public:
28  BlockableHostResolver()
29      : event_(true, false),
30        was_request_cancelled_(false) {
31  }
32
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
50  virtual void CancelRequest(RequestHandle req) {
51    EXPECT_EQ(reinterpret_cast<RequestHandle*>(1), req);
52    was_request_cancelled_ = true;
53  }
54
55  virtual void AddObserver(Observer* observer) {
56    NOTREACHED();
57  }
58
59  virtual void RemoveObserver(Observer* observer) {
60    NOTREACHED();
61  }
62
63  // Waits until Resolve() has been called.
64  void WaitUntilRequestIsReceived() {
65    event_.Wait();
66  }
67
68  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().
80class SyncProxyResolver : public ProxyResolver {
81 public:
82  explicit SyncProxyResolver(SyncHostResolverBridge* host_resolver)
83      : ProxyResolver(false), host_resolver_(host_resolver) {}
84
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
104  virtual void CancelRequest(RequestHandle request) {
105    NOTREACHED();
106  }
107
108  virtual void Shutdown() {
109    host_resolver_->Shutdown();
110  }
111
112  virtual void CancelSetPacScript() {
113    NOTREACHED();
114  }
115
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
126class SyncProxyResolverFactory : public ProxyResolverFactory {
127 public:
128  // Takes ownership of |sync_host_resolver|.
129  explicit SyncProxyResolverFactory(SyncHostResolverBridge* sync_host_resolver)
130      : ProxyResolverFactory(false),
131        sync_host_resolver_(sync_host_resolver) {
132  }
133
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.
145class IOThread : public base::Thread {
146 public:
147  IOThread() : base::Thread("IO-thread") {}
148
149  virtual ~IOThread() {
150    Stop();
151  }
152
153  BlockableHostResolver* async_resolver() {
154    return async_resolver_.get();
155  }
156
157 protected:
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
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.
216TEST(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