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.
5 #include "net/proxy/polling_proxy_config_service.h"
8 #include "base/location.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/observer_list.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/synchronization/lock.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/threading/worker_pool.h"
15 #include "net/proxy/proxy_config.h"
19 // Reference-counted wrapper that does all the work (needs to be
20 // reference-counted since we post tasks between threads; may outlive
21 // the parent PollingProxyConfigService).
22 class PollingProxyConfigService::Core
23 : public base::RefCountedThreadSafe
<PollingProxyConfigService::Core
> {
25 Core(base::TimeDelta poll_interval
, GetConfigFunction get_config_func
)
26 : get_config_func_(get_config_func
),
27 poll_interval_(poll_interval
),
28 have_initialized_origin_runner_(false),
30 poll_task_outstanding_(false),
31 poll_task_queued_(false) {}
33 // Called when the parent PollingProxyConfigService is destroyed
34 // (observers should not be called past this point).
36 base::AutoLock
l(lock_
);
37 origin_task_runner_
= NULL
;
40 bool GetLatestProxyConfig(ProxyConfig
* config
) {
41 LazyInitializeOriginLoop();
42 DCHECK(origin_task_runner_
->BelongsToCurrentThread());
46 // If we have already retrieved the proxy settings (on worker thread)
47 // then return what we last saw.
49 *config
= last_config_
;
55 void AddObserver(Observer
* observer
) {
56 LazyInitializeOriginLoop();
57 DCHECK(origin_task_runner_
->BelongsToCurrentThread());
58 observers_
.AddObserver(observer
);
61 void RemoveObserver(Observer
* observer
) {
62 DCHECK(origin_task_runner_
->BelongsToCurrentThread());
63 observers_
.RemoveObserver(observer
);
66 // Check for a new configuration if enough time has elapsed.
68 LazyInitializeOriginLoop();
69 DCHECK(origin_task_runner_
->BelongsToCurrentThread());
71 if (last_poll_time_
.is_null() ||
72 (base::TimeTicks::Now() - last_poll_time_
) > poll_interval_
) {
77 void CheckForChangesNow() {
78 LazyInitializeOriginLoop();
79 DCHECK(origin_task_runner_
->BelongsToCurrentThread());
81 if (poll_task_outstanding_
) {
82 // Only allow one task to be outstanding at a time. If we get a poll
83 // request while we are busy, we will defer it until the current poll
85 poll_task_queued_
= true;
89 last_poll_time_
= base::TimeTicks::Now();
90 poll_task_outstanding_
= true;
91 poll_task_queued_
= false;
92 base::WorkerPool::PostTask(
94 base::Bind(&Core::PollOnWorkerThread
, this, get_config_func_
),
99 friend class base::RefCountedThreadSafe
<Core
>;
102 void PollOnWorkerThread(GetConfigFunction func
) {
106 base::AutoLock
l(lock_
);
107 if (origin_task_runner_
.get()) {
108 origin_task_runner_
->PostTask(
109 FROM_HERE
, base::Bind(&Core::GetConfigCompleted
, this, config
));
113 // Called after the worker thread has finished retrieving a configuration.
114 void GetConfigCompleted(const ProxyConfig
& config
) {
115 DCHECK(poll_task_outstanding_
);
116 poll_task_outstanding_
= false;
118 if (!origin_task_runner_
.get())
119 return; // Was orphaned (parent has already been destroyed).
121 DCHECK(origin_task_runner_
->BelongsToCurrentThread());
123 if (!has_config_
|| !last_config_
.Equals(config
)) {
124 // If the configuration has changed, notify the observers.
126 last_config_
= config
;
127 FOR_EACH_OBSERVER(Observer
, observers_
,
128 OnProxyConfigChanged(config
,
129 ProxyConfigService::CONFIG_VALID
));
132 if (poll_task_queued_
)
133 CheckForChangesNow();
136 void LazyInitializeOriginLoop() {
137 // TODO(eroman): Really this should be done in the constructor, but right
138 // now chrome is constructing the ProxyConfigService on the
139 // UI thread so we can't cache the IO thread for the purpose
140 // of DCHECKs until the first call is made.
141 if (!have_initialized_origin_runner_
) {
142 origin_task_runner_
= base::ThreadTaskRunnerHandle::Get();
143 have_initialized_origin_runner_
= true;
147 GetConfigFunction get_config_func_
;
148 base::ObserverList
<Observer
> observers_
;
149 ProxyConfig last_config_
;
150 base::TimeTicks last_poll_time_
;
151 base::TimeDelta poll_interval_
;
154 scoped_refptr
<base::SingleThreadTaskRunner
> origin_task_runner_
;
156 bool have_initialized_origin_runner_
;
158 bool poll_task_outstanding_
;
159 bool poll_task_queued_
;
162 void PollingProxyConfigService::AddObserver(Observer
* observer
) {
163 core_
->AddObserver(observer
);
166 void PollingProxyConfigService::RemoveObserver(Observer
* observer
) {
167 core_
->RemoveObserver(observer
);
170 ProxyConfigService::ConfigAvailability
171 PollingProxyConfigService::GetLatestProxyConfig(ProxyConfig
* config
) {
172 return core_
->GetLatestProxyConfig(config
) ? CONFIG_VALID
: CONFIG_PENDING
;
175 void PollingProxyConfigService::OnLazyPoll() {
179 PollingProxyConfigService::PollingProxyConfigService(
180 base::TimeDelta poll_interval
,
181 GetConfigFunction get_config_func
)
182 : core_(new Core(poll_interval
, get_config_func
)) {
185 PollingProxyConfigService::~PollingProxyConfigService() {
189 void PollingProxyConfigService::CheckForChangesNow() {
190 core_
->CheckForChangesNow();