Chromium Code Reviews| Index: chrome/browser/policy/async_policy_loader.cc |
| diff --git a/chrome/browser/policy/async_policy_loader.cc b/chrome/browser/policy/async_policy_loader.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..be1a0c8ba0074cf318d86a6e1bf2f4d6c7e1eaad |
| --- /dev/null |
| +++ b/chrome/browser/policy/async_policy_loader.cc |
| @@ -0,0 +1,170 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/policy/async_policy_loader.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/bind_helpers.h" |
| +#include "base/file_path.h" |
| +#include "base/files/file_path_watcher.h" |
| +#include "chrome/browser/policy/policy_bundle.h" |
| +#include "content/public/browser/browser_thread.h" |
| + |
| +using base::Time; |
| +using base::TimeDelta; |
| +using content::BrowserThread; |
| + |
| +namespace policy { |
| + |
| +namespace { |
| + |
| +// Amount of time to wait for the files on disk to settle before trying to load |
| +// them. This alleviates the problem of reading partially written files and |
| +// makes it possible to batch quasi-simultaneous changes. |
| +const int kSettleIntervalSeconds = 5; |
| + |
| +// The time interval for rechecking policy. This is the fallback in case the |
| +// implementation never detects changes. |
| +const int kReloadIntervalSeconds = 15 * 60; |
| + |
| +// This delegate is owned by the FilePathWatcher. It lives in FILE and notifies |
| +// the AsyncPolicyLoader through the |reload_callback|, which is invalidated |
| +// when the AsyncPolicyLoader is deleted. |
|
Mattias Nissler (ping if slow)
2012/06/04 08:48:26
Note: net/dns/file_path_watcher_wrapper.h does mos
Joao da Silva
2012/06/04 14:05:06
Moved this to FilePathWatcher in another CL.
|
| +class FilePathWatcherDelegate : public base::files::FilePathWatcher::Delegate { |
| + public: |
| + explicit FilePathWatcherDelegate(const base::Closure& reload_callback) |
| + : reload_callback_(reload_callback) {} |
| + |
| + // FilePathWatcher::Delegate implementation. |
| + virtual void OnFilePathChanged(const FilePath& path) OVERRIDE { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + reload_callback_.Run(); |
| + } |
| + |
| + virtual void OnFilePathError(const FilePath& path) OVERRIDE { |
| + DLOG(ERROR) << "FilePathWatcher error: " << path.value(); |
| + } |
| + |
| + private: |
| + virtual ~FilePathWatcherDelegate() {} |
| + |
| + base::Closure reload_callback_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(FilePathWatcherDelegate); |
| +}; |
| + |
| +} // namespace |
| + |
| +AsyncPolicyLoader::AsyncPolicyLoader() |
| + : ALLOW_THIS_IN_INITIALIZER_LIST(reload_weak_factory_(this)), |
| + ALLOW_THIS_IN_INITIALIZER_LIST(watchers_weak_factory_(this)) {} |
| + |
| +AsyncPolicyLoader::~AsyncPolicyLoader() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| +} |
| + |
| +base::Time AsyncPolicyLoader::LastModificationTime() { |
| + return base::Time(); |
| +} |
| + |
| +void AsyncPolicyLoader::Reload(bool force) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + |
| + TimeDelta delay; |
| + Time now = Time::Now(); |
| + // Check if there was a recent modification to the underlying files. |
| + if (!force && !IsSafeToReload(now, &delay)) { |
| + ScheduleNextReload(delay); |
| + return; |
| + } |
| + |
| + scoped_ptr<PolicyBundle> bundle(Load()); |
| + |
| + // Check if there was a modification while reading. |
| + if (!force && !IsSafeToReload(now, &delay)) { |
| + ScheduleNextReload(delay); |
| + return; |
| + } |
| + |
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| + base::Bind(update_callback_, base::Passed(&bundle))); |
| + ScheduleNextReload(TimeDelta::FromSeconds(kReloadIntervalSeconds)); |
| +} |
| + |
| +void AsyncPolicyLoader::WatchPath(const FilePath& path) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + base::files::FilePathWatcher* watcher = new base::files::FilePathWatcher(); |
| + base::Closure reload_callback = |
| + base::Bind(&AsyncPolicyLoader::Reload, |
| + watchers_weak_factory_.GetWeakPtr(), |
| + false /* force */); |
| + if (watcher->Watch(path, new FilePathWatcherDelegate(reload_callback))) { |
|
Mattias Nissler (ping if slow)
2012/06/04 08:48:26
no need for curlies
Joao da Silva
2012/06/04 14:05:06
Done.
|
| + watchers_.push_back(watcher); |
| + } else { |
| + DLOG(ERROR) << "FilePathWatcher error adding watch: " << path.value(); |
| + } |
| +} |
| + |
| +scoped_ptr<PolicyBundle> AsyncPolicyLoader::InitialLoad() { |
| + // This is the first load, early during startup. Use this to record the |
| + // initial |last_modification_time_|, so that potential changes made before |
| + // installing the watches can be detected. |
| + last_modification_time_ = LastModificationTime(); |
| + return Load(); |
| +} |
| + |
| +void AsyncPolicyLoader::Init( |
| + const UpdateCallback& update_callback) { |
|
Mattias Nissler (ping if slow)
2012/06/04 08:48:26
fits previous line
Joao da Silva
2012/06/04 14:05:06
Done.
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + DCHECK(update_callback_.is_null()); |
| + DCHECK(!update_callback.is_null()); |
| + update_callback_ = update_callback; |
| + |
| + InitOnFile(); |
| + |
| + // There might have been changes to the underlying files since the initial |
| + // load and before the watchers have been created. |
| + if (LastModificationTime() != last_modification_time_) |
| + Reload(false); |
| + |
| + // Start periodic refreshes. |
| + ScheduleNextReload(TimeDelta::FromSeconds(kReloadIntervalSeconds)); |
| +} |
| + |
| +void AsyncPolicyLoader::ScheduleNextReload(TimeDelta delay) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + reload_weak_factory_.InvalidateWeakPtrs(); |
| + BrowserThread::PostDelayedTask( |
| + BrowserThread::FILE, FROM_HERE, |
| + base::Bind(&AsyncPolicyLoader::Reload, |
| + reload_weak_factory_.GetWeakPtr(), |
|
Mattias Nissler (ping if slow)
2012/06/04 08:48:26
indentation
Joao da Silva
2012/06/04 14:05:06
Done.
|
| + false /* force */), |
| + delay); |
| +} |
| + |
| +bool AsyncPolicyLoader::IsSafeToReload(const Time& now, TimeDelta* delay) { |
| + Time last_modification = LastModificationTime(); |
| + if (last_modification.is_null()) |
| + return true; |
| + |
| + // If there was a change since the last recorded modification, wait some more. |
| + TimeDelta kSettleInterval(TimeDelta::FromSeconds(kSettleIntervalSeconds)); |
|
Mattias Nissler (ping if slow)
2012/06/04 08:48:26
const
Joao da Silva
2012/06/04 14:05:06
Done.
|
| + if (last_modification != last_modification_time_) { |
| + last_modification_time_ = last_modification; |
| + last_modification_clock_ = now; |
| + *delay = kSettleInterval; |
| + return false; |
| + } |
| + |
| + // Check whether the settle interval has elapsed. |
| + base::TimeDelta age = now - last_modification_clock_; |
| + if (age < kSettleInterval) { |
| + *delay = kSettleInterval - age; |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +} // namespace policy |