• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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_resolution/polling_proxy_config_service.h"
6 
7 #include <memory>
8 
9 #include "base/functional/bind.h"
10 #include "base/location.h"
11 #include "base/observer_list.h"
12 #include "base/synchronization/lock.h"
13 #include "base/task/single_thread_task_runner.h"
14 #include "base/task/thread_pool.h"
15 #include "net/proxy_resolution/proxy_config_with_annotation.h"
16 
17 namespace net {
18 
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> {
24  public:
Core(base::TimeDelta poll_interval,GetConfigFunction get_config_func,const NetworkTrafficAnnotationTag & traffic_annotation)25   Core(base::TimeDelta poll_interval,
26        GetConfigFunction get_config_func,
27        const NetworkTrafficAnnotationTag& traffic_annotation)
28       : get_config_func_(get_config_func),
29         poll_interval_(poll_interval),
30         traffic_annotation_(traffic_annotation) {}
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 lock(lock_);
36     origin_task_runner_ = nullptr;
37   }
38 
GetLatestProxyConfig(ProxyConfigWithAnnotation * config)39   bool GetLatestProxyConfig(ProxyConfigWithAnnotation* config) {
40     LazyInitializeOriginLoop();
41     DCHECK(origin_task_runner_->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_task_runner_->BelongsToCurrentThread());
57     observers_.AddObserver(observer);
58   }
59 
RemoveObserver(Observer * observer)60   void RemoveObserver(Observer* observer) {
61     DCHECK(origin_task_runner_->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_task_runner_->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_task_runner_->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::ThreadPool::PostTask(
92         FROM_HERE,
93         {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
94         base::BindOnce(&Core::PollAsync, this, get_config_func_));
95   }
96 
97  private:
98   friend class base::RefCountedThreadSafe<Core>;
99   ~Core() = default;
100 
PollAsync(GetConfigFunction func)101   void PollAsync(GetConfigFunction func) {
102     ProxyConfigWithAnnotation config;
103     func(traffic_annotation_, &config);
104 
105     base::AutoLock lock(lock_);
106     if (origin_task_runner_.get()) {
107       origin_task_runner_->PostTask(
108           FROM_HERE, base::BindOnce(&Core::GetConfigCompleted, this, config));
109     }
110   }
111 
112   // Called after the worker thread has finished retrieving a configuration.
GetConfigCompleted(const ProxyConfigWithAnnotation & config)113   void GetConfigCompleted(const ProxyConfigWithAnnotation& config) {
114     DCHECK(poll_task_outstanding_);
115     poll_task_outstanding_ = false;
116 
117     if (!origin_task_runner_.get())
118       return;  // Was orphaned (parent has already been destroyed).
119 
120     DCHECK(origin_task_runner_->BelongsToCurrentThread());
121 
122     if (!has_config_ || !last_config_.value().Equals(config.value())) {
123       // If the configuration has changed, notify the observers.
124       has_config_ = true;
125       last_config_ = config;
126       for (auto& observer : observers_)
127         observer.OnProxyConfigChanged(config, ProxyConfigService::CONFIG_VALID);
128     }
129 
130     if (poll_task_queued_)
131       CheckForChangesNow();
132   }
133 
LazyInitializeOriginLoop()134   void LazyInitializeOriginLoop() {
135     // TODO(eroman): Really this should be done in the constructor, but some
136     //               consumers constructing the ProxyConfigService on threads
137     //               other than the ProxyConfigService's main thread, so we
138     //               can't cache the main thread for the purpose of DCHECKs
139     //               until the first call is made.
140     if (!have_initialized_origin_runner_) {
141       origin_task_runner_ = base::SingleThreadTaskRunner::GetCurrentDefault();
142       have_initialized_origin_runner_ = true;
143     }
144   }
145 
146   GetConfigFunction get_config_func_;
147   base::ObserverList<Observer>::Unchecked observers_;
148   ProxyConfigWithAnnotation last_config_;
149   base::TimeTicks last_poll_time_;
150   base::TimeDelta poll_interval_;
151 
152   const NetworkTrafficAnnotationTag traffic_annotation_;
153 
154   base::Lock lock_;
155   scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
156 
157   bool have_initialized_origin_runner_ = false;
158   bool has_config_ = false;
159   bool poll_task_outstanding_ = false;
160   bool poll_task_queued_ = false;
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(ProxyConfigWithAnnotation * config)172 PollingProxyConfigService::GetLatestProxyConfig(
173     ProxyConfigWithAnnotation* config) {
174   return core_->GetLatestProxyConfig(config) ? CONFIG_VALID : CONFIG_PENDING;
175 }
176 
OnLazyPoll()177 void PollingProxyConfigService::OnLazyPoll() {
178   core_->OnLazyPoll();
179 }
180 
UsesPolling()181 bool PollingProxyConfigService::UsesPolling() {
182   return true;
183 }
184 
PollingProxyConfigService(base::TimeDelta poll_interval,GetConfigFunction get_config_func,const NetworkTrafficAnnotationTag & traffic_annotation)185 PollingProxyConfigService::PollingProxyConfigService(
186     base::TimeDelta poll_interval,
187     GetConfigFunction get_config_func,
188     const NetworkTrafficAnnotationTag& traffic_annotation)
189     : core_(base::MakeRefCounted<Core>(poll_interval,
190                                        get_config_func,
191                                        traffic_annotation)) {}
192 
~PollingProxyConfigService()193 PollingProxyConfigService::~PollingProxyConfigService() {
194   core_->Orphan();
195 }
196 
CheckForChangesNow()197 void PollingProxyConfigService::CheckForChangesNow() {
198   core_->CheckForChangesNow();
199 }
200 
201 }  // namespace net
202