• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AsyncPolicyProvider(SchemaRegistry * registry,scoped_ptr<AsyncPolicyLoader> loader)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 
~AsyncPolicyProvider()28 AsyncPolicyProvider::~AsyncPolicyProvider() {
29   DCHECK(CalledOnValidThread());
30   // Shutdown() must have been called before.
31   DCHECK(!loader_);
32 }
33 
Init(SchemaRegistry * registry)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 
Shutdown()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 
RefreshPolicies()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 
ReloadAfterRefreshSync()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 
OnLoaderReloaded(scoped_ptr<PolicyBundle> bundle)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
LoaderUpdateCallback(scoped_refptr<base::MessageLoopProxy> loop,base::WeakPtr<AsyncPolicyProvider> weak_this,scoped_ptr<PolicyBundle> bundle)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