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: 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). 36 void Orphan() { 37 base::AutoLock l(lock_); 38 origin_loop_proxy_ = NULL; 39 } 40 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 56 void AddObserver(Observer* observer) { 57 LazyInitializeOriginLoop(); 58 DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); 59 observers_.AddObserver(observer); 60 } 61 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. 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 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>; 101 ~Core() {} 102 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. 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 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 163 void PollingProxyConfigService::AddObserver(Observer* observer) { 164 core_->AddObserver(observer); 165 } 166 167 void PollingProxyConfigService::RemoveObserver(Observer* observer) { 168 core_->RemoveObserver(observer); 169 } 170 171 ProxyConfigService::ConfigAvailability 172 PollingProxyConfigService::GetLatestProxyConfig(ProxyConfig* config) { 173 return core_->GetLatestProxyConfig(config) ? CONFIG_VALID : CONFIG_PENDING; 174 } 175 176 void PollingProxyConfigService::OnLazyPoll() { 177 core_->OnLazyPoll(); 178 } 179 180 PollingProxyConfigService::PollingProxyConfigService( 181 base::TimeDelta poll_interval, 182 GetConfigFunction get_config_func) 183 : core_(new Core(poll_interval, get_config_func)) { 184 } 185 186 PollingProxyConfigService::~PollingProxyConfigService() { 187 core_->Orphan(); 188 } 189 190 void PollingProxyConfigService::CheckForChangesNow() { 191 core_->CheckForChangesNow(); 192 } 193 194 } // namespace net 195