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