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