Revert 207377 "Disable TabCaptureApiTest.EndToEnd for Linux (non..."
[chromium-blink-merge.git] / remoting / host / config_file_watcher.cc
blobaab5fb9db6e299c9a41c02f72dbf04127e2a497b
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.
5 #include "remoting/host/config_file_watcher.h"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/files/file_path_watcher.h"
12 #include "base/file_util.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/timer.h"
18 namespace remoting {
20 // The name of the command-line switch used to specify the host configuration
21 // file to use.
22 const char kHostConfigSwitchName[] = "host-config";
24 const base::FilePath::CharType kDefaultHostConfigFile[] =
25 FILE_PATH_LITERAL("host.json");
27 // Maximum number of times to try reading the configuration file before
28 // reporting an error.
29 const int kMaxRetries = 3;
31 class ConfigFileWatcherImpl
32 : public base::RefCountedThreadSafe<ConfigFileWatcherImpl> {
33 public:
34 // Creates a configuration file watcher that lives on the |io_task_runner|
35 // thread but posts config file updates on on |main_task_runner|.
36 ConfigFileWatcherImpl(
37 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
38 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
39 ConfigFileWatcher::Delegate* delegate);
41 // Starts watching |config_path|.
42 void Watch(const base::FilePath& config_path);
44 // Stops watching the configuration file.
45 void StopWatching();
47 private:
48 friend class base::RefCountedThreadSafe<ConfigFileWatcherImpl>;
49 virtual ~ConfigFileWatcherImpl();
51 void FinishStopping();
53 // Called every time the host configuration file is updated.
54 void OnConfigUpdated(const base::FilePath& path, bool error);
56 // Reads the configuration file and passes it to the delegate.
57 void ReloadConfig();
59 std::string config_;
60 base::FilePath config_path_;
62 scoped_ptr<base::DelayTimer<ConfigFileWatcherImpl> > config_updated_timer_;
64 // Number of times an attempt to read the configuration file failed.
65 int retries_;
67 // Monitors the host configuration file.
68 scoped_ptr<base::FilePathWatcher> config_watcher_;
70 base::WeakPtrFactory<ConfigFileWatcher::Delegate> delegate_weak_factory_;
71 base::WeakPtr<ConfigFileWatcher::Delegate> delegate_;
73 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
74 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
76 DISALLOW_COPY_AND_ASSIGN(ConfigFileWatcherImpl);
79 ConfigFileWatcher::Delegate::~Delegate() {
82 ConfigFileWatcher::ConfigFileWatcher(
83 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
84 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
85 Delegate* delegate)
86 : impl_(new ConfigFileWatcherImpl(main_task_runner,
87 io_task_runner, delegate)) {
90 ConfigFileWatcher::~ConfigFileWatcher() {
91 impl_->StopWatching();
92 impl_ = NULL;
95 void ConfigFileWatcher::Watch(const base::FilePath& config_path) {
96 impl_->Watch(config_path);
99 ConfigFileWatcherImpl::ConfigFileWatcherImpl(
100 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
101 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
102 ConfigFileWatcher::Delegate* delegate)
103 : retries_(0),
104 delegate_weak_factory_(delegate),
105 delegate_(delegate_weak_factory_.GetWeakPtr()),
106 main_task_runner_(main_task_runner),
107 io_task_runner_(io_task_runner) {
108 DCHECK(main_task_runner_->BelongsToCurrentThread());
111 void ConfigFileWatcherImpl::Watch(const base::FilePath& config_path) {
112 if (!io_task_runner_->BelongsToCurrentThread()) {
113 io_task_runner_->PostTask(
114 FROM_HERE,
115 base::Bind(&ConfigFileWatcherImpl::Watch, this, config_path));
116 return;
119 DCHECK(config_path_.empty());
120 DCHECK(!config_updated_timer_);
121 DCHECK(!config_watcher_);
123 // Create the timer that will be used for delayed-reading the configuration
124 // file.
125 config_updated_timer_.reset(new base::DelayTimer<ConfigFileWatcherImpl>(
126 FROM_HERE, base::TimeDelta::FromSeconds(2), this,
127 &ConfigFileWatcherImpl::ReloadConfig));
129 // Start watching the configuration file.
130 config_watcher_.reset(new base::FilePathWatcher());
131 config_path_ = config_path;
132 if (!config_watcher_->Watch(
133 config_path_, false,
134 base::Bind(&ConfigFileWatcherImpl::OnConfigUpdated, this))) {
135 PLOG(ERROR) << "Couldn't watch file '" << config_path_.value() << "'";
136 main_task_runner_->PostTask(
137 FROM_HERE,
138 base::Bind(&ConfigFileWatcher::Delegate::OnConfigWatcherError,
139 delegate_));
140 return;
143 // Force reloading of the configuration file at least once.
144 ReloadConfig();
147 void ConfigFileWatcherImpl::StopWatching() {
148 DCHECK(main_task_runner_->BelongsToCurrentThread());
150 delegate_weak_factory_.InvalidateWeakPtrs();
151 io_task_runner_->PostTask(
152 FROM_HERE, base::Bind(&ConfigFileWatcherImpl::FinishStopping, this));
155 ConfigFileWatcherImpl::~ConfigFileWatcherImpl() {
156 DCHECK(!config_updated_timer_);
157 DCHECK(!config_watcher_);
160 void ConfigFileWatcherImpl::FinishStopping() {
161 DCHECK(io_task_runner_->BelongsToCurrentThread());
163 config_updated_timer_.reset();
164 config_watcher_.reset();
167 void ConfigFileWatcherImpl::OnConfigUpdated(const base::FilePath& path,
168 bool error) {
169 DCHECK(io_task_runner_->BelongsToCurrentThread());
171 // Call ReloadConfig() after a short delay, so that we will not try to read
172 // the updated configuration file before it has been completely written.
173 // If the writer moves the new configuration file into place atomically,
174 // this delay may not be necessary.
175 if (!error && config_path_ == path)
176 config_updated_timer_->Reset();
179 void ConfigFileWatcherImpl::ReloadConfig() {
180 DCHECK(io_task_runner_->BelongsToCurrentThread());
182 std::string config;
183 if (!file_util::ReadFileToString(config_path_, &config)) {
184 #if defined(OS_WIN)
185 // EACCESS may indicate a locking or sharing violation. Retry a few times
186 // before reporting an error.
187 if (errno == EACCES && retries_ < kMaxRetries) {
188 PLOG(WARNING) << "Failed to read '" << config_path_.value() << "'";
190 retries_ += 1;
191 config_updated_timer_->Reset();
192 return;
194 #endif // defined(OS_WIN)
196 PLOG(ERROR) << "Failed to read '" << config_path_.value() << "'";
197 main_task_runner_->PostTask(
198 FROM_HERE,
199 base::Bind(&ConfigFileWatcher::Delegate::OnConfigWatcherError,
200 delegate_));
201 return;
204 retries_ = 0;
206 // Post an updated configuration only if it has actually changed.
207 if (config_ != config) {
208 config_ = config;
209 main_task_runner_->PostTask(
210 FROM_HERE,
211 base::Bind(&ConfigFileWatcher::Delegate::OnConfigUpdated, delegate_,
212 config_));
216 } // namespace remoting