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