Home | History | Annotate | Download | only in proxy
      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