| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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: | |
| 28 BlockableHostResolver() | |
| 29 : event_(true, false), | |
| 30 was_request_cancelled_(false) { | |
| 31 } | |
| 32 | |
| 33 virtual int Resolve(const RequestInfo& info, | |
| 34 AddressList* addresses, | |
| 35 const CompletionCallback& callback, | |
| 36 RequestHandle* out_req, | |
| 37 const BoundNetLog& net_log) OVERRIDE { | |
| 38 EXPECT_FALSE(callback.is_null()); | |
| 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 int ResolveFromCache(const RequestInfo& info, | |
| 51 AddressList* addresses, | |
| 52 const BoundNetLog& net_log) OVERRIDE { | |
| 53 NOTIMPLEMENTED(); | |
| 54 return ERR_UNEXPECTED; | |
| 55 } | |
| 56 | |
| 57 virtual void CancelRequest(RequestHandle req) OVERRIDE { | |
| 58 EXPECT_EQ(reinterpret_cast<RequestHandle*>(1), req); | |
| 59 was_request_cancelled_ = true; | |
| 60 } | |
| 61 | |
| 62 // Waits until Resolve() has been called. | |
| 63 void WaitUntilRequestIsReceived() { | |
| 64 event_.Wait(); | |
| 65 } | |
| 66 | |
| 67 bool was_request_cancelled() const { | |
| 68 return was_request_cancelled_; | |
| 69 } | |
| 70 | |
| 71 private: | |
| 72 // Event to notify when a resolve request was received. | |
| 73 base::WaitableEvent event_; | |
| 74 bool was_request_cancelled_; | |
| 75 }; | |
| 76 | |
| 77 // This implementation of ProxyResolver simply does a synchronous resolve | |
| 78 // on |host_resolver| in response to GetProxyForURL(). | |
| 79 class SyncProxyResolver : public ProxyResolver { | |
| 80 public: | |
| 81 explicit SyncProxyResolver(SyncHostResolverBridge* host_resolver) | |
| 82 : ProxyResolver(false), host_resolver_(host_resolver) {} | |
| 83 | |
| 84 virtual int GetProxyForURL(const GURL& url, | |
| 85 ProxyInfo* results, | |
| 86 const CompletionCallback& callback, | |
| 87 RequestHandle* request, | |
| 88 const BoundNetLog& net_log) { | |
| 89 EXPECT_FALSE(!callback.is_null()); | |
| 90 EXPECT_FALSE(request); | |
| 91 | |
| 92 // Do a synchronous host resolve. | |
| 93 HostResolver::RequestInfo info(HostPortPair::FromURL(url)); | |
| 94 AddressList addresses; | |
| 95 int rv = host_resolver_->Resolve(info, &addresses, net_log); | |
| 96 | |
| 97 EXPECT_EQ(ERR_ABORTED, rv); | |
| 98 | |
| 99 return rv; | |
| 100 } | |
| 101 | |
| 102 virtual void CancelRequest(RequestHandle request) OVERRIDE { | |
| 103 NOTREACHED(); | |
| 104 } | |
| 105 | |
| 106 virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE { | |
| 107 NOTREACHED(); | |
| 108 return LOAD_STATE_IDLE; | |
| 109 } | |
| 110 | |
| 111 virtual LoadState GetLoadStateThreadSafe( | |
| 112 RequestHandle request) const OVERRIDE { | |
| 113 NOTREACHED(); | |
| 114 return LOAD_STATE_IDLE; | |
| 115 } | |
| 116 | |
| 117 virtual void Shutdown() OVERRIDE { | |
| 118 host_resolver_->Shutdown(); | |
| 119 } | |
| 120 | |
| 121 virtual void CancelSetPacScript() OVERRIDE { | |
| 122 NOTREACHED(); | |
| 123 } | |
| 124 | |
| 125 virtual int SetPacScript( | |
| 126 const scoped_refptr<ProxyResolverScriptData>& script_data, | |
| 127 const CompletionCallback& callback) OVERRIDE { | |
| 128 return OK; | |
| 129 } | |
| 130 | |
| 131 private: | |
| 132 SyncHostResolverBridge* const host_resolver_; | |
| 133 }; | |
| 134 | |
| 135 class SyncProxyResolverFactory : public ProxyResolverFactory { | |
| 136 public: | |
| 137 // Takes ownership of |sync_host_resolver|. | |
| 138 explicit SyncProxyResolverFactory(SyncHostResolverBridge* sync_host_resolver) | |
| 139 : ProxyResolverFactory(false), | |
| 140 sync_host_resolver_(sync_host_resolver) { | |
| 141 } | |
| 142 | |
| 143 virtual ProxyResolver* CreateProxyResolver() OVERRIDE { | |
| 144 return new SyncProxyResolver(sync_host_resolver_.get()); | |
| 145 } | |
| 146 | |
| 147 private: | |
| 148 const scoped_ptr<SyncHostResolverBridge> sync_host_resolver_; | |
| 149 }; | |
| 150 | |
| 151 // This helper thread is used to create the circumstances for the deadlock. | |
| 152 // It is analagous to the "IO thread" which would be main thread running the | |
| 153 // network stack. | |
| 154 class IOThread : public base::Thread { | |
| 155 public: | |
| 156 IOThread() : base::Thread("IO-thread") {} | |
| 157 | |
| 158 virtual ~IOThread() { | |
| 159 Stop(); | |
| 160 } | |
| 161 | |
| 162 BlockableHostResolver* async_resolver() { | |
| 163 return async_resolver_.get(); | |
| 164 } | |
| 165 | |
| 166 protected: | |
| 167 virtual void Init() OVERRIDE { | |
| 168 async_resolver_.reset(new BlockableHostResolver()); | |
| 169 | |
| 170 // Create a synchronous host resolver that operates the async host | |
| 171 // resolver on THIS thread. | |
| 172 SyncHostResolverBridge* sync_resolver = | |
| 173 new SyncHostResolverBridge(async_resolver_.get(), message_loop()); | |
| 174 | |
| 175 proxy_resolver_.reset( | |
| 176 new MultiThreadedProxyResolver( | |
| 177 new SyncProxyResolverFactory(sync_resolver), | |
| 178 1u)); | |
| 179 | |
| 180 // Initialize the resolver. | |
| 181 TestCompletionCallback callback; | |
| 182 proxy_resolver_->SetPacScript(ProxyResolverScriptData::FromURL(GURL()), | |
| 183 callback.callback()); | |
| 184 EXPECT_EQ(OK, callback.WaitForResult()); | |
| 185 | |
| 186 // Start an asynchronous request to the proxy resolver | |
| 187 // (note that it will never complete). | |
| 188 proxy_resolver_->GetProxyForURL( | |
| 189 GURL("http://test/"), &results_, callback_.callback(), &request_, | |
| 190 BoundNetLog()); | |
| 191 } | |
| 192 | |
| 193 virtual void CleanUp() OVERRIDE { | |
| 194 // Cancel the outstanding request (note however that this will not | |
| 195 // unblock the PAC thread though). | |
| 196 proxy_resolver_->CancelRequest(request_); | |
| 197 | |
| 198 // Delete the single threaded proxy resolver. | |
| 199 proxy_resolver_.reset(); | |
| 200 | |
| 201 // (There may have been a completion posted back to origin thread, avoid | |
| 202 // leaking it by running). | |
| 203 MessageLoop::current()->RunUntilIdle(); | |
| 204 | |
| 205 // During the teardown sequence of the single threaded proxy resolver, | |
| 206 // the outstanding host resolve should have been cancelled. | |
| 207 EXPECT_TRUE(async_resolver_->was_request_cancelled()); | |
| 208 } | |
| 209 | |
| 210 private: | |
| 211 // This (async) host resolver will outlive the thread that is operating it | |
| 212 // synchronously. | |
| 213 scoped_ptr<BlockableHostResolver> async_resolver_; | |
| 214 | |
| 215 scoped_ptr<ProxyResolver> proxy_resolver_; | |
| 216 | |
| 217 // Data for the outstanding request to the single threaded proxy resolver. | |
| 218 TestCompletionCallback callback_; | |
| 219 ProxyInfo results_; | |
| 220 ProxyResolver::RequestHandle request_; | |
| 221 }; | |
| 222 | |
| 223 // Test that a deadlock does not happen during shutdown when a host resolve | |
| 224 // is outstanding on the SyncHostResolverBridge. | |
| 225 // This is a regression test for http://crbug.com/41244. | |
| 226 TEST(MultiThreadedProxyResolverTest, ShutdownIsCalledBeforeThreadJoin) { | |
| 227 IOThread io_thread; | |
| 228 base::Thread::Options options; | |
| 229 options.message_loop_type = MessageLoop::TYPE_IO; | |
| 230 ASSERT_TRUE(io_thread.StartWithOptions(options)); | |
| 231 | |
| 232 io_thread.async_resolver()->WaitUntilRequestIsReceived(); | |
| 233 | |
| 234 // Now upon exitting this scope, the IOThread is destroyed -- this will | |
| 235 // stop the IOThread, which will in turn delete the | |
| 236 // SingleThreadedProxyResolver, which in turn will stop its internal | |
| 237 // PAC thread (which is currently blocked waiting on the host resolve which | |
| 238 // is running on IOThread). The IOThread::Cleanup() will verify that after | |
| 239 // the PAC thread is stopped, it cancels the request on the HostResolver. | |
| 240 } | |
| 241 | |
| 242 } // namespace | |
| 243 | |
| 244 } // namespace net | |
| OLD | NEW |