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/polling_proxy_config_service.h"
6
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "base/observer_list.h"
12 #include "base/synchronization/lock.h"
13 #include "base/threading/worker_pool.h"
14 #include "net/proxy/proxy_config.h"
15
16 namespace net {
17
18 // Reference-counted wrapper that does all the work (needs to be
19 // reference-counted since we post tasks between threads; may outlive
20 // the parent PollingProxyConfigService).
21 class PollingProxyConfigService::Core
22 : public base::RefCountedThreadSafe<PollingProxyConfigService::Core> {
23 public:
Core(base::TimeDelta poll_interval,GetConfigFunction get_config_func)24 Core(base::TimeDelta poll_interval,
25 GetConfigFunction get_config_func)
26 : get_config_func_(get_config_func),
27 poll_interval_(poll_interval),
28 have_initialized_origin_loop_(false),
29 has_config_(false),
30 poll_task_outstanding_(false),
31 poll_task_queued_(false) {
32 }
33
34 // Called when the parent PollingProxyConfigService is destroyed
35 // (observers should not be called past this point).
Orphan()36 void Orphan() {
37 base::AutoLock l(lock_);
38 origin_loop_proxy_ = NULL;
39 }
40
GetLatestProxyConfig(ProxyConfig * config)41 bool GetLatestProxyConfig(ProxyConfig* config) {
42 LazyInitializeOriginLoop();
43 DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
44
45 OnLazyPoll();
46
47 // If we have already retrieved the proxy settings (on worker thread)
48 // then return what we last saw.
49 if (has_config_) {
50 *config = last_config_;
51 return true;
52 }
53 return false;
54 }
55
AddObserver(Observer * observer)56 void AddObserver(Observer* observer) {
57 LazyInitializeOriginLoop();
58 DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
59 observers_.AddObserver(observer);
60 }
61
RemoveObserver(Observer * observer)62 void RemoveObserver(Observer* observer) {
63 DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
64 observers_.RemoveObserver(observer);
65 }
66
67 // Check for a new configuration if enough time has elapsed.
OnLazyPoll()68 void OnLazyPoll() {
69 LazyInitializeOriginLoop();
70 DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
71
72 if (last_poll_time_.is_null() ||
73 (base::TimeTicks::Now() - last_poll_time_) > poll_interval_) {
74 CheckForChangesNow();
75 }
76 }
77
CheckForChangesNow()78 void CheckForChangesNow() {
79 LazyInitializeOriginLoop();
80 DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
81
82 if (poll_task_outstanding_) {
83 // Only allow one task to be outstanding at a time. If we get a poll
84 // request while we are busy, we will defer it until the current poll
85 // completes.
86 poll_task_queued_ = true;
87 return;
88 }
89
90 last_poll_time_ = base::TimeTicks::Now();
91 poll_task_outstanding_ = true;
92 poll_task_queued_ = false;
93 base::WorkerPool::PostTask(
94 FROM_HERE,
95 base::Bind(&Core::PollOnWorkerThread, this, get_config_func_),
96 true);
97 }
98
99 private:
100 friend class base::RefCountedThreadSafe<Core>;
~Core()101 ~Core() {}
102
PollOnWorkerThread(GetConfigFunction func)103 void PollOnWorkerThread(GetConfigFunction func) {
104 ProxyConfig config;
105 func(&config);
106
107 base::AutoLock l(lock_);
108 if (origin_loop_proxy_.get()) {
109 origin_loop_proxy_->PostTask(
110 FROM_HERE, base::Bind(&Core::GetConfigCompleted, this, config));
111 }
112 }
113
114 // Called after the worker thread has finished retrieving a configuration.
GetConfigCompleted(const ProxyConfig & config)115 void GetConfigCompleted(const ProxyConfig& config) {
116 DCHECK(poll_task_outstanding_);
117 poll_task_outstanding_ = false;
118
119 if (!origin_loop_proxy_.get())
120 return; // Was orphaned (parent has already been destroyed).
121
122 DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
123
124 if (!has_config_ || !last_config_.Equals(config)) {
125 // If the configuration has changed, notify the observers.
126 has_config_ = true;
127 last_config_ = config;
128 FOR_EACH_OBSERVER(Observer, observers_,
129 OnProxyConfigChanged(config,
130 ProxyConfigService::CONFIG_VALID));
131 }
132
133 if (poll_task_queued_)
134 CheckForChangesNow();
135 }
136
LazyInitializeOriginLoop()137 void LazyInitializeOriginLoop() {
138 // TODO(eroman): Really this should be done in the constructor, but right
139 // now chrome is constructing the ProxyConfigService on the
140 // UI thread so we can't cache the IO thread for the purpose
141 // of DCHECKs until the first call is made.
142 if (!have_initialized_origin_loop_) {
143 origin_loop_proxy_ = base::MessageLoopProxy::current();
144 have_initialized_origin_loop_ = true;
145 }
146 }
147
148 GetConfigFunction get_config_func_;
149 ObserverList<Observer> observers_;
150 ProxyConfig last_config_;
151 base::TimeTicks last_poll_time_;
152 base::TimeDelta poll_interval_;
153
154 base::Lock lock_;
155 scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_;
156
157 bool have_initialized_origin_loop_;
158 bool has_config_;
159 bool poll_task_outstanding_;
160 bool poll_task_queued_;
161 };
162
AddObserver(Observer * observer)163 void PollingProxyConfigService::AddObserver(Observer* observer) {
164 core_->AddObserver(observer);
165 }
166
RemoveObserver(Observer * observer)167 void PollingProxyConfigService::RemoveObserver(Observer* observer) {
168 core_->RemoveObserver(observer);
169 }
170
171 ProxyConfigService::ConfigAvailability
GetLatestProxyConfig(ProxyConfig * config)172 PollingProxyConfigService::GetLatestProxyConfig(ProxyConfig* config) {
173 return core_->GetLatestProxyConfig(config) ? CONFIG_VALID : CONFIG_PENDING;
174 }
175
OnLazyPoll()176 void PollingProxyConfigService::OnLazyPoll() {
177 core_->OnLazyPoll();
178 }
179
PollingProxyConfigService(base::TimeDelta poll_interval,GetConfigFunction get_config_func)180 PollingProxyConfigService::PollingProxyConfigService(
181 base::TimeDelta poll_interval,
182 GetConfigFunction get_config_func)
183 : core_(new Core(poll_interval, get_config_func)) {
184 }
185
~PollingProxyConfigService()186 PollingProxyConfigService::~PollingProxyConfigService() {
187 core_->Orphan();
188 }
189
CheckForChangesNow()190 void PollingProxyConfigService::CheckForChangesNow() {
191 core_->CheckForChangesNow();
192 }
193
194 } // namespace net
195