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