• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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