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/compiler_specific.h"
8#include "base/logging.h"
9#include "base/message_loop.h"
10#include "base/synchronization/lock.h"
11#include "base/synchronization/waitable_event.h"
12#include "net/base/net_errors.h"
13#include "net/base/net_log.h"
14
15namespace net {
16
17// SyncHostResolverBridge::Core ----------------------------------------------
18
19class SyncHostResolverBridge::Core
20    : public base::RefCountedThreadSafe<SyncHostResolverBridge::Core> {
21 public:
22  Core(HostResolver* resolver, MessageLoop* host_resolver_loop);
23
24  int ResolveSynchronously(const HostResolver::RequestInfo& info,
25                           AddressList* addresses);
26
27  // Returns true if Shutdown() has been called.
28  bool HasShutdown() const {
29    base::AutoLock l(lock_);
30    return HasShutdownLocked();
31  }
32
33  // Called on |host_resolver_loop_|.
34  void Shutdown();
35
36 private:
37  friend class base::RefCountedThreadSafe<SyncHostResolverBridge::Core>;
38
39  bool HasShutdownLocked() const {
40    return has_shutdown_;
41  }
42
43  // Called on |host_resolver_loop_|.
44  void StartResolve(const HostResolver::RequestInfo& info,
45                    AddressList* addresses);
46
47  // Called on |host_resolver_loop_|.
48  void OnResolveCompletion(int result);
49
50  // Not called on |host_resolver_loop_|.
51  int WaitForResolveCompletion();
52
53  HostResolver* const host_resolver_;
54  MessageLoop* const host_resolver_loop_;
55  net::CompletionCallbackImpl<Core> callback_;
56  // The result from the current request (set on |host_resolver_loop_|).
57  int err_;
58  // The currently outstanding request to |host_resolver_|, or NULL.
59  HostResolver::RequestHandle outstanding_request_;
60
61  // Event to notify completion of resolve request.  We always Signal() on
62  // |host_resolver_loop_| and Wait() on a different thread.
63  base::WaitableEvent event_;
64
65  // True if Shutdown() has been called. Must hold |lock_| to access it.
66  bool has_shutdown_;
67
68  // Mutex to guard accesses to |has_shutdown_|.
69      mutable base::Lock lock_;
70
71  DISALLOW_COPY_AND_ASSIGN(Core);
72};
73
74SyncHostResolverBridge::Core::Core(HostResolver* host_resolver,
75                                   MessageLoop* host_resolver_loop)
76    : host_resolver_(host_resolver),
77      host_resolver_loop_(host_resolver_loop),
78      ALLOW_THIS_IN_INITIALIZER_LIST(
79          callback_(this, &Core::OnResolveCompletion)),
80      err_(0),
81      outstanding_request_(NULL),
82      event_(true, false),
83      has_shutdown_(false) {}
84
85int SyncHostResolverBridge::Core::ResolveSynchronously(
86    const HostResolver::RequestInfo& info,
87    net::AddressList* addresses) {
88  // Otherwise start an async resolve on the resolver's thread.
89  host_resolver_loop_->PostTask(
90      FROM_HERE,
91      NewRunnableMethod(this, &Core::StartResolve,
92                        info, addresses));
93
94  return WaitForResolveCompletion();
95}
96
97void SyncHostResolverBridge::Core::StartResolve(
98    const HostResolver::RequestInfo& info,
99    net::AddressList* addresses) {
100  DCHECK_EQ(MessageLoop::current(), host_resolver_loop_);
101  DCHECK(!outstanding_request_);
102
103  if (HasShutdown())
104    return;
105
106  int error = host_resolver_->Resolve(
107      info, addresses, &callback_, &outstanding_request_, BoundNetLog());
108  if (error != ERR_IO_PENDING)
109    OnResolveCompletion(error);  // Completed synchronously.
110}
111
112void SyncHostResolverBridge::Core::OnResolveCompletion(int result) {
113  DCHECK_EQ(MessageLoop::current(), host_resolver_loop_);
114  err_ = result;
115  outstanding_request_ = NULL;
116  event_.Signal();
117}
118
119int SyncHostResolverBridge::Core::WaitForResolveCompletion() {
120  DCHECK_NE(MessageLoop::current(), host_resolver_loop_);
121  event_.Wait();
122
123  {
124    base::AutoLock l(lock_);
125    if (HasShutdownLocked())
126      return ERR_ABORTED;
127    event_.Reset();
128  }
129
130  return err_;
131}
132
133void SyncHostResolverBridge::Core::Shutdown() {
134  DCHECK_EQ(MessageLoop::current(), host_resolver_loop_);
135
136  if (outstanding_request_) {
137    host_resolver_->CancelRequest(outstanding_request_);
138    outstanding_request_ = NULL;
139  }
140
141  {
142    base::AutoLock l(lock_);
143    has_shutdown_ = true;
144  }
145
146  // Wake up the PAC thread in case it was waiting for resolve completion.
147  event_.Signal();
148}
149
150// SyncHostResolverBridge -----------------------------------------------------
151
152SyncHostResolverBridge::SyncHostResolverBridge(HostResolver* host_resolver,
153                                               MessageLoop* host_resolver_loop)
154    : host_resolver_loop_(host_resolver_loop),
155      core_(new Core(host_resolver, host_resolver_loop)) {
156  DCHECK(host_resolver_loop_);
157}
158
159SyncHostResolverBridge::~SyncHostResolverBridge() {
160  DCHECK(core_->HasShutdown());
161}
162
163int SyncHostResolverBridge::Resolve(const RequestInfo& info,
164                                    AddressList* addresses,
165                                    CompletionCallback* callback,
166                                    RequestHandle* out_req,
167                                    const BoundNetLog& net_log) {
168  DCHECK(!callback);
169  DCHECK(!out_req);
170
171  return core_->ResolveSynchronously(info, addresses);
172}
173
174void SyncHostResolverBridge::CancelRequest(RequestHandle req) {
175  NOTREACHED();
176}
177
178void SyncHostResolverBridge::AddObserver(Observer* observer) {
179  NOTREACHED();
180}
181
182void SyncHostResolverBridge::RemoveObserver(Observer* observer) {
183  NOTREACHED();
184}
185
186void SyncHostResolverBridge::Shutdown() {
187  DCHECK_EQ(MessageLoop::current(), host_resolver_loop_);
188  core_->Shutdown();
189}
190
191}  // namespace net
192