| // Copyright (c) 2010 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/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); |
| |
| // 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>; |
| |
| bool HasShutdownLocked() const { |
| return has_shutdown_; |
| } |
| |
| // Called on |host_resolver_loop_|. |
| void StartResolve(const HostResolver::RequestInfo& info, |
| AddressList* addresses); |
| |
| // 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_; |
| net::CompletionCallbackImpl<Core> callback_; |
| // 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), |
| ALLOW_THIS_IN_INITIALIZER_LIST( |
| callback_(this, &Core::OnResolveCompletion)), |
| err_(0), |
| outstanding_request_(NULL), |
| event_(true, false), |
| has_shutdown_(false) {} |
| |
| int SyncHostResolverBridge::Core::ResolveSynchronously( |
| const HostResolver::RequestInfo& info, |
| net::AddressList* addresses) { |
| // Otherwise start an async resolve on the resolver's thread. |
| host_resolver_loop_->PostTask( |
| FROM_HERE, |
| NewRunnableMethod(this, &Core::StartResolve, |
| info, addresses)); |
| |
| return WaitForResolveCompletion(); |
| } |
| |
| void SyncHostResolverBridge::Core::StartResolve( |
| const HostResolver::RequestInfo& info, |
| net::AddressList* addresses) { |
| DCHECK_EQ(MessageLoop::current(), host_resolver_loop_); |
| DCHECK(!outstanding_request_); |
| |
| if (HasShutdown()) |
| return; |
| |
| int error = host_resolver_->Resolve( |
| info, addresses, &callback_, &outstanding_request_, BoundNetLog()); |
| 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 RequestInfo& info, |
| AddressList* addresses, |
| CompletionCallback* callback, |
| RequestHandle* out_req, |
| const BoundNetLog& net_log) { |
| DCHECK(!callback); |
| DCHECK(!out_req); |
| |
| return core_->ResolveSynchronously(info, addresses); |
| } |
| |
| void SyncHostResolverBridge::CancelRequest(RequestHandle req) { |
| NOTREACHED(); |
| } |
| |
| void SyncHostResolverBridge::AddObserver(Observer* observer) { |
| NOTREACHED(); |
| } |
| |
| void SyncHostResolverBridge::RemoveObserver(Observer* observer) { |
| NOTREACHED(); |
| } |
| |
| void SyncHostResolverBridge::Shutdown() { |
| DCHECK_EQ(MessageLoop::current(), host_resolver_loop_); |
| core_->Shutdown(); |
| } |
| |
| } // namespace net |