• 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.Run(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 
121     DCHECK(origin_task_runner_->BelongsToCurrentThread());
122 
123     if (!has_config_ || !last_config_.value().Equals(config.value())) {
124       // If the configuration has changed, notify the observers.
125       has_config_ = true;
126       last_config_ = config;
127       for (auto& observer : observers_) {
128         observer.OnProxyConfigChanged(config, ProxyConfigService::CONFIG_VALID);
129       }
130     }
131 
132     if (poll_task_queued_) {
133       CheckForChangesNow();
134     }
135   }
136 
LazyInitializeOriginLoop()137   void LazyInitializeOriginLoop() {
138     // TODO(eroman): Really this should be done in the constructor, but some
139     //               consumers constructing the ProxyConfigService on threads
140     //               other than the ProxyConfigService's main thread, so we
141     //               can't cache the main thread for the purpose of DCHECKs
142     //               until the first call is made.
143     if (!have_initialized_origin_runner_) {
144       origin_task_runner_ = base::SingleThreadTaskRunner::GetCurrentDefault();
145       have_initialized_origin_runner_ = true;
146     }
147   }
148 
149   GetConfigFunction get_config_func_;
150   base::ObserverList<Observer>::Unchecked observers_;
151   ProxyConfigWithAnnotation last_config_;
152   base::TimeTicks last_poll_time_;
153   base::TimeDelta poll_interval_;
154 
155   const NetworkTrafficAnnotationTag traffic_annotation_;
156 
157   base::Lock lock_;
158   scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
159 
160   bool have_initialized_origin_runner_ = false;
161   bool has_config_ = false;
162   bool poll_task_outstanding_ = false;
163   bool poll_task_queued_ = false;
164 };
165 
AddObserver(Observer * observer)166 void PollingProxyConfigService::AddObserver(Observer* observer) {
167   core_->AddObserver(observer);
168 }
169 
RemoveObserver(Observer * observer)170 void PollingProxyConfigService::RemoveObserver(Observer* observer) {
171   core_->RemoveObserver(observer);
172 }
173 
174 ProxyConfigService::ConfigAvailability
GetLatestProxyConfig(ProxyConfigWithAnnotation * config)175 PollingProxyConfigService::GetLatestProxyConfig(
176     ProxyConfigWithAnnotation* config) {
177   return core_->GetLatestProxyConfig(config) ? CONFIG_VALID : CONFIG_PENDING;
178 }
179 
OnLazyPoll()180 void PollingProxyConfigService::OnLazyPoll() {
181   core_->OnLazyPoll();
182 }
183 
UsesPolling()184 bool PollingProxyConfigService::UsesPolling() {
185   return true;
186 }
187 
PollingProxyConfigService(base::TimeDelta poll_interval,GetConfigFunction get_config_func,const NetworkTrafficAnnotationTag & traffic_annotation)188 PollingProxyConfigService::PollingProxyConfigService(
189     base::TimeDelta poll_interval,
190     GetConfigFunction get_config_func,
191     const NetworkTrafficAnnotationTag& traffic_annotation)
192     : core_(base::MakeRefCounted<Core>(poll_interval,
193                                        get_config_func,
194                                        traffic_annotation)) {}
195 
~PollingProxyConfigService()196 PollingProxyConfigService::~PollingProxyConfigService() {
197   core_->Orphan();
198 }
199 
CheckForChangesNow()200 void PollingProxyConfigService::CheckForChangesNow() {
201   core_->CheckForChangesNow();
202 }
203 
204 }  // namespace net
205