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 14namespace 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). 19class PollingProxyConfigService::Core 20 : public base::RefCountedThreadSafe<PollingProxyConfigService::Core> { 21 public: 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). 34 void Orphan() { 35 base::AutoLock l(lock_); 36 origin_loop_proxy_ = NULL; 37 } 38 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 54 void AddObserver(Observer* observer) { 55 LazyInitializeOriginLoop(); 56 DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); 57 observers_.AddObserver(observer); 58 } 59 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. 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 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: 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. 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 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 159void PollingProxyConfigService::AddObserver(Observer* observer) { 160 core_->AddObserver(observer); 161} 162 163void PollingProxyConfigService::RemoveObserver(Observer* observer) { 164 core_->RemoveObserver(observer); 165} 166 167ProxyConfigService::ConfigAvailability 168 PollingProxyConfigService::GetLatestProxyConfig(ProxyConfig* config) { 169 return core_->GetLatestProxyConfig(config) ? CONFIG_VALID : CONFIG_PENDING; 170} 171 172void PollingProxyConfigService::OnLazyPoll() { 173 core_->OnLazyPoll(); 174} 175 176PollingProxyConfigService::PollingProxyConfigService( 177 base::TimeDelta poll_interval, 178 GetConfigFunction get_config_func) 179 : core_(new Core(poll_interval, get_config_func)) { 180} 181 182PollingProxyConfigService::~PollingProxyConfigService() { 183 core_->Orphan(); 184} 185 186void PollingProxyConfigService::CheckForChangesNow() { 187 core_->CheckForChangesNow(); 188} 189 190} // namespace net 191