| 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 |