OLD | NEW |
| (Empty) |
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 "chrome/browser/policy/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 "chrome/browser/policy/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 | |
OLD | NEW |