diff options
author | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-23 06:02:40 +0000 |
---|---|---|
committer | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-23 06:02:40 +0000 |
commit | 119655003d8f225282179043e990df879062e529 (patch) | |
tree | 4ee907ddfb8e308a00b5bb9b624e072b028623b6 /net/proxy/polling_proxy_config_service.cc | |
parent | dacc2c255ae3f823e4a39d975e97c067a76dacf9 (diff) | |
download | chromium_src-119655003d8f225282179043e990df879062e529.zip chromium_src-119655003d8f225282179043e990df879062e529.tar.gz chromium_src-119655003d8f225282179043e990df879062e529.tar.bz2 |
Change the ProxyConfigService interface to be asynchronous, and support observers.
The Windows implementation is still using a polling mechanism under the hood, however that polling has been moved to the worker pool so it won't block the IO thread in case WinHttpGetIEProxyConfigForCurrentUser is slow (crbug.com/12189).
BUG=12189
Review URL: http://codereview.chromium.org/3056011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53442 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/proxy/polling_proxy_config_service.cc')
-rw-r--r-- | net/proxy/polling_proxy_config_service.cc | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/net/proxy/polling_proxy_config_service.cc b/net/proxy/polling_proxy_config_service.cc new file mode 100644 index 0000000..ac42d9c --- /dev/null +++ b/net/proxy/polling_proxy_config_service.cc @@ -0,0 +1,166 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/proxy/polling_proxy_config_service.h" + +#include "base/lock.h" +#include "base/message_loop_proxy.h" +#include "base/observer_list.h" +#include "base/scoped_ptr.h" +#include "base/worker_pool.h" +#include "net/proxy/proxy_config.h" + +namespace net { + +// Reference-counted wrapper that does all the work (needs to be +// reference-counted since we post tasks between threads; may outlive +// the parent PollingProxyConfigService). +class PollingProxyConfigService::Core + : public base::RefCountedThreadSafe<PollingProxyConfigService::Core> { + public: + Core(base::TimeDelta poll_interval, + GetConfigFunction get_config_func) + : get_config_func_(get_config_func), + has_config_(false), + poll_task_outstanding_(false), + poll_interval_(poll_interval), + have_initialized_origin_loop_(false) { + } + + // Called when the parent PollingProxyConfigService is destroyed + // (observers should not be called past this point). + void Orphan() { + AutoLock l(lock_); + origin_loop_proxy_ = NULL; + } + + bool GetLatestProxyConfig(ProxyConfig* config) { + LazyInitializeOriginLoop(); + DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + + OnLazyPoll(); + + // If we have already retrieved the proxy settings (on worker thread) + // then return what we last saw. + if (has_config_) { + *config = last_config_; + return true; + } + return false; + } + + void AddObserver(Observer* observer) { + LazyInitializeOriginLoop(); + DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + observers_.AddObserver(observer); + } + + void RemoveObserver(Observer* observer) { + DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + observers_.RemoveObserver(observer); + } + + // Check for a new configuration if enough time has elapsed. + void OnLazyPoll() { + LazyInitializeOriginLoop(); + DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + + if (poll_task_outstanding_) + return; // Still waiting for earlier test to finish. + + base::TimeTicks now = base::TimeTicks::Now(); + + if (last_poll_time_.is_null() || + (now - last_poll_time_) > poll_interval_) { + last_poll_time_ = now; + poll_task_outstanding_ = true; + WorkerPool::PostTask( + FROM_HERE, + NewRunnableMethod( + this, &Core::PollOnWorkerThread, get_config_func_), true); + } + } + + private: + void PollOnWorkerThread(GetConfigFunction func) { + ProxyConfig config; + func(&config); + + AutoLock l(lock_); + if (origin_loop_proxy_) { + origin_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &Core::GetConfigCompleted, config)); + } + } + + // Called after the worker thread has finished retrieving a configuration. + void GetConfigCompleted(const ProxyConfig& config) { + DCHECK(poll_task_outstanding_); + poll_task_outstanding_ = false; + + if (!origin_loop_proxy_) + return; // Was orphaned (parent has already been destroyed). + + DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + + if (!has_config_ || !last_config_.Equals(config)) { + // If the configuration has changed, notify the observers. + has_config_ = true; + last_config_ = config; + FOR_EACH_OBSERVER(Observer, observers_, OnProxyConfigChanged(config)); + } + } + + void LazyInitializeOriginLoop() { + // TODO(eroman): Really this should be done in the constructor, but right + // now chrome is constructing the ProxyConfigService on the + // UI thread so we can't cache the IO thread for the purpose + // of DCHECKs until the first call is made. + if (!have_initialized_origin_loop_) { + origin_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread(); + have_initialized_origin_loop_ = true; + } + } + + GetConfigFunction get_config_func_; + ObserverList<Observer> observers_; + bool has_config_; + bool poll_task_outstanding_; + ProxyConfig last_config_; + base::TimeTicks last_poll_time_; + base::TimeDelta poll_interval_; + bool have_initialized_origin_loop_; + + Lock lock_; + scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_; +}; + +PollingProxyConfigService::PollingProxyConfigService( + base::TimeDelta poll_interval, + GetConfigFunction get_config_func) + : core_(new Core(poll_interval, get_config_func)) { +} + +PollingProxyConfigService::~PollingProxyConfigService() { + core_->Orphan(); +} + +void PollingProxyConfigService::AddObserver(Observer* observer) { + core_->AddObserver(observer); +} + +void PollingProxyConfigService::RemoveObserver(Observer* observer) { + core_->RemoveObserver(observer); +} + +bool PollingProxyConfigService::GetLatestProxyConfig(ProxyConfig* config) { + return core_->GetLatestProxyConfig(config); +} + +void PollingProxyConfigService::OnLazyPoll() { + core_->OnLazyPoll(); +} + +} // namespace net |