| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/proxy/sync_host_resolver_bridge.h" |
| |
| #include "base/bind.h" |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "base/message_loop.h" |
| #include "base/synchronization/lock.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/net_log.h" |
| |
| namespace net { |
| |
| // SyncHostResolverBridge::Core ---------------------------------------------- |
| |
| class SyncHostResolverBridge::Core |
| : public base::RefCountedThreadSafe<SyncHostResolverBridge::Core> { |
| public: |
| Core(HostResolver* resolver, MessageLoop* host_resolver_loop); |
| |
| int ResolveSynchronously(const HostResolver::RequestInfo& info, |
| AddressList* addresses, |
| const BoundNetLog& net_log); |
| |
| // Returns true if Shutdown() has been called. |
| bool HasShutdown() const { |
| base::AutoLock l(lock_); |
| return HasShutdownLocked(); |
| } |
| |
| // Called on |host_resolver_loop_|. |
| void Shutdown(); |
| |
| private: |
| friend class base::RefCountedThreadSafe<SyncHostResolverBridge::Core>; |
| ~Core() {} |
| |
| bool HasShutdownLocked() const { |
| return has_shutdown_; |
| } |
| |
| // Called on |host_resolver_loop_|. |
| void StartResolve(const HostResolver::RequestInfo& info, |
| AddressList* addresses, |
| const BoundNetLog& net_log); |
| |
| // Called on |host_resolver_loop_|. |
| void OnResolveCompletion(int result); |
| |
| // Not called on |host_resolver_loop_|. |
| int WaitForResolveCompletion(); |
| |
| HostResolver* const host_resolver_; |
| MessageLoop* const host_resolver_loop_; |
| // The result from the current request (set on |host_resolver_loop_|). |
| int err_; |
| // The currently outstanding request to |host_resolver_|, or NULL. |
| HostResolver::RequestHandle outstanding_request_; |
| |
| // Event to notify completion of resolve request. We always Signal() on |
| // |host_resolver_loop_| and Wait() on a different thread. |
| base::WaitableEvent event_; |
| |
| // True if Shutdown() has been called. Must hold |lock_| to access it. |
| bool has_shutdown_; |
| |
| // Mutex to guard accesses to |has_shutdown_|. |
| mutable base::Lock lock_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Core); |
| }; |
| |
| SyncHostResolverBridge::Core::Core(HostResolver* host_resolver, |
| MessageLoop* host_resolver_loop) |
| : host_resolver_(host_resolver), |
| host_resolver_loop_(host_resolver_loop), |
| err_(0), |
| outstanding_request_(NULL), |
| event_(true, false), |
| has_shutdown_(false) {} |
| |
| int SyncHostResolverBridge::Core::ResolveSynchronously( |
| const HostResolver::RequestInfo& info, |
| net::AddressList* addresses, |
| const BoundNetLog& net_log) { |
| // Otherwise start an async resolve on the resolver's thread. |
| host_resolver_loop_->PostTask( |
| FROM_HERE, |
| base::Bind(&Core::StartResolve, this, info, addresses, net_log)); |
| |
| return WaitForResolveCompletion(); |
| } |
| |
| void SyncHostResolverBridge::Core::StartResolve( |
| const HostResolver::RequestInfo& info, |
| net::AddressList* addresses, |
| const BoundNetLog& net_log) { |
| DCHECK_EQ(MessageLoop::current(), host_resolver_loop_); |
| DCHECK(!outstanding_request_); |
| |
| if (HasShutdown()) |
| return; |
| |
| int error = host_resolver_->Resolve( |
| info, addresses, base::Bind(&Core::OnResolveCompletion, this), |
| &outstanding_request_, net_log); |
| if (error != ERR_IO_PENDING) |
| OnResolveCompletion(error); // Completed synchronously. |
| } |
| |
| void SyncHostResolverBridge::Core::OnResolveCompletion(int result) { |
| DCHECK_EQ(MessageLoop::current(), host_resolver_loop_); |
| err_ = result; |
| outstanding_request_ = NULL; |
| event_.Signal(); |
| } |
| |
| int SyncHostResolverBridge::Core::WaitForResolveCompletion() { |
| DCHECK_NE(MessageLoop::current(), host_resolver_loop_); |
| event_.Wait(); |
| |
| { |
| base::AutoLock l(lock_); |
| if (HasShutdownLocked()) |
| return ERR_ABORTED; |
| event_.Reset(); |
| } |
| |
| return err_; |
| } |
| |
| void SyncHostResolverBridge::Core::Shutdown() { |
| DCHECK_EQ(MessageLoop::current(), host_resolver_loop_); |
| |
| if (outstanding_request_) { |
| host_resolver_->CancelRequest(outstanding_request_); |
| outstanding_request_ = NULL; |
| } |
| |
| { |
| base::AutoLock l(lock_); |
| has_shutdown_ = true; |
| } |
| |
| // Wake up the PAC thread in case it was waiting for resolve completion. |
| event_.Signal(); |
| } |
| |
| // SyncHostResolverBridge ----------------------------------------------------- |
| |
| SyncHostResolverBridge::SyncHostResolverBridge(HostResolver* host_resolver, |
| MessageLoop* host_resolver_loop) |
| : host_resolver_loop_(host_resolver_loop), |
| core_(new Core(host_resolver, host_resolver_loop)) { |
| DCHECK(host_resolver_loop_); |
| } |
| |
| SyncHostResolverBridge::~SyncHostResolverBridge() { |
| DCHECK(core_->HasShutdown()); |
| } |
| |
| int SyncHostResolverBridge::Resolve(const HostResolver::RequestInfo& info, |
| AddressList* addresses, |
| const BoundNetLog& net_log) { |
| return core_->ResolveSynchronously(info, addresses, net_log); |
| } |
| |
| void SyncHostResolverBridge::Shutdown() { |
| DCHECK_EQ(MessageLoop::current(), host_resolver_loop_); |
| core_->Shutdown(); |
| } |
| |
| } // namespace net |