Home | History | Annotate | Download | only in proxy
      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:
     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 
    159 void PollingProxyConfigService::AddObserver(Observer* observer) {
    160   core_->AddObserver(observer);
    161 }
    162 
    163 void PollingProxyConfigService::RemoveObserver(Observer* observer) {
    164   core_->RemoveObserver(observer);
    165 }
    166 
    167 ProxyConfigService::ConfigAvailability
    168     PollingProxyConfigService::GetLatestProxyConfig(ProxyConfig* config) {
    169   return core_->GetLatestProxyConfig(config) ? CONFIG_VALID : CONFIG_PENDING;
    170 }
    171 
    172 void PollingProxyConfigService::OnLazyPoll() {
    173   core_->OnLazyPoll();
    174 }
    175 
    176 PollingProxyConfigService::PollingProxyConfigService(
    177     base::TimeDelta poll_interval,
    178     GetConfigFunction get_config_func)
    179     : core_(new Core(poll_interval, get_config_func)) {
    180 }
    181 
    182 PollingProxyConfigService::~PollingProxyConfigService() {
    183   core_->Orphan();
    184 }
    185 
    186 void PollingProxyConfigService::CheckForChangesNow() {
    187   core_->CheckForChangesNow();
    188 }
    189 
    190 }  // namespace net
    191