Home | History | Annotate | Download | only in common
      1 // Copyright 2013 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 "components/policy/core/common/async_policy_provider.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/location.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/message_loop/message_loop_proxy.h"
     12 #include "base/sequenced_task_runner.h"
     13 #include "components/policy/core/common/async_policy_loader.h"
     14 #include "components/policy/core/common/policy_bundle.h"
     15 #include "components/policy/core/common/schema_registry.h"
     16 
     17 namespace policy {
     18 
     19 AsyncPolicyProvider::AsyncPolicyProvider(
     20     SchemaRegistry* registry,
     21     scoped_ptr<AsyncPolicyLoader> loader)
     22     : loader_(loader.release()),
     23       weak_factory_(this) {
     24   // Make an immediate synchronous load on startup.
     25   OnLoaderReloaded(loader_->InitialLoad(registry->schema_map()));
     26 }
     27 
     28 AsyncPolicyProvider::~AsyncPolicyProvider() {
     29   DCHECK(CalledOnValidThread());
     30   // Shutdown() must have been called before.
     31   DCHECK(!loader_);
     32 }
     33 
     34 void AsyncPolicyProvider::Init(SchemaRegistry* registry) {
     35   DCHECK(CalledOnValidThread());
     36   ConfigurationPolicyProvider::Init(registry);
     37 
     38   if (!loader_)
     39     return;
     40 
     41   AsyncPolicyLoader::UpdateCallback callback =
     42       base::Bind(&AsyncPolicyProvider::LoaderUpdateCallback,
     43                  base::MessageLoopProxy::current(),
     44                  weak_factory_.GetWeakPtr());
     45   bool post = loader_->task_runner()->PostTask(
     46       FROM_HERE,
     47       base::Bind(&AsyncPolicyLoader::Init,
     48                  base::Unretained(loader_),
     49                  callback));
     50   DCHECK(post) << "AsyncPolicyProvider::Init() called with threads not running";
     51 }
     52 
     53 void AsyncPolicyProvider::Shutdown() {
     54   DCHECK(CalledOnValidThread());
     55   // Note on the lifetime of |loader_|:
     56   // The |loader_| lives on the background thread, and is deleted from here.
     57   // This means that posting tasks on the |loader_| to the background thread
     58   // from the AsyncPolicyProvider is always safe, since a potential DeleteSoon()
     59   // is only posted from here. The |loader_| posts back to the
     60   // AsyncPolicyProvider through the |update_callback_|, which has a WeakPtr to
     61   // |this|.
     62   if (!loader_->task_runner()->DeleteSoon(FROM_HERE, loader_)) {
     63     // The background thread doesn't exist; this only happens on unit tests.
     64     delete loader_;
     65   }
     66   loader_ = NULL;
     67   ConfigurationPolicyProvider::Shutdown();
     68 }
     69 
     70 void AsyncPolicyProvider::RefreshPolicies() {
     71   DCHECK(CalledOnValidThread());
     72 
     73   // Subtle: RefreshPolicies() has a contract that requires the next policy
     74   // update notification (triggered from UpdatePolicy()) to reflect any changes
     75   // made before this call. So if a caller has modified the policy settings and
     76   // invoked RefreshPolicies(), then by the next notification these policies
     77   // should already be provided.
     78   // However, it's also possible that an asynchronous Reload() is in progress
     79   // and just posted OnLoaderReloaded(). Therefore a task is posted to the
     80   // background thread before posting the next Reload, to prevent a potential
     81   // concurrent Reload() from triggering a notification too early. If another
     82   // refresh task has been posted, it is invalidated now.
     83   if (!loader_)
     84     return;
     85   refresh_callback_.Reset(
     86       base::Bind(&AsyncPolicyProvider::ReloadAfterRefreshSync,
     87                  weak_factory_.GetWeakPtr()));
     88   loader_->task_runner()->PostTaskAndReply(
     89       FROM_HERE,
     90       base::Bind(base::DoNothing),
     91       refresh_callback_.callback());
     92 }
     93 
     94 void AsyncPolicyProvider::ReloadAfterRefreshSync() {
     95   DCHECK(CalledOnValidThread());
     96   // This task can only enter if it was posted from RefreshPolicies(), and it
     97   // hasn't been cancelled meanwhile by another call to RefreshPolicies().
     98   DCHECK(!refresh_callback_.IsCancelled());
     99   // There can't be another refresh callback pending now, since its creation
    100   // in RefreshPolicies() would have cancelled the current execution. So it's
    101   // safe to cancel the |refresh_callback_| now, so that OnLoaderReloaded()
    102   // sees that there is no refresh pending.
    103   refresh_callback_.Cancel();
    104 
    105   if (!loader_)
    106     return;
    107 
    108   loader_->task_runner()->PostTask(
    109       FROM_HERE,
    110       base::Bind(&AsyncPolicyLoader::RefreshPolicies,
    111                  base::Unretained(loader_),
    112                  schema_map()));
    113 }
    114 
    115 void AsyncPolicyProvider::OnLoaderReloaded(scoped_ptr<PolicyBundle> bundle) {
    116   DCHECK(CalledOnValidThread());
    117   // Only propagate policy updates if there are no pending refreshes, and if
    118   // Shutdown() hasn't been called yet.
    119   if (refresh_callback_.IsCancelled() && loader_)
    120     UpdatePolicy(bundle.Pass());
    121 }
    122 
    123 // static
    124 void AsyncPolicyProvider::LoaderUpdateCallback(
    125     scoped_refptr<base::MessageLoopProxy> loop,
    126     base::WeakPtr<AsyncPolicyProvider> weak_this,
    127     scoped_ptr<PolicyBundle> bundle) {
    128   loop->PostTask(FROM_HERE,
    129                  base::Bind(&AsyncPolicyProvider::OnLoaderReloaded,
    130                             weak_this,
    131                             base::Passed(&bundle)));
    132 }
    133 
    134 }  // namespace policy
    135