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: 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(). 80 class 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 126 class 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. 145 class 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. 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