15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "remoting/host/config_file_watcher.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h" 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind_helpers.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h" 12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/files/file_path_watcher.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/weak_ptr.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/single_thread_task_runner.h" 16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/timer/timer.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace remoting { 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The name of the command-line switch used to specify the host configuration 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// file to use. 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kHostConfigSwitchName[] = "host-config"; 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const base::FilePath::CharType kDefaultHostConfigFile[] = 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FILE_PATH_LITERAL("host.json"); 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Maximum number of times to try reading the configuration file before 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// reporting an error. 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kMaxRetries = 3; 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ConfigFileWatcherImpl 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : public base::RefCountedThreadSafe<ConfigFileWatcherImpl> { 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Creates a configuration file watcher that lives on the |io_task_runner| 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // thread but posts config file updates on on |main_task_runner|. 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ConfigFileWatcherImpl( 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ConfigFileWatcher::Delegate* delegate); 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Starts watching |config_path|. 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void Watch(const base::FilePath& config_path); 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Stops watching the configuration file. 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void StopWatching(); 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) friend class base::RefCountedThreadSafe<ConfigFileWatcherImpl>; 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual ~ConfigFileWatcherImpl(); 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void FinishStopping(); 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Called every time the host configuration file is updated. 542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void OnConfigUpdated(const base::FilePath& path, bool error); 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Reads the configuration file and passes it to the delegate. 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void ReloadConfig(); 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string config_; 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath config_path_; 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<base::DelayTimer<ConfigFileWatcherImpl> > config_updated_timer_; 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Number of times an attempt to read the configuration file failed. 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int retries_; 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Monitors the host configuration file. 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) scoped_ptr<base::FilePathWatcher> config_watcher_; 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::WeakPtrFactory<ConfigFileWatcher::Delegate> delegate_weak_factory_; 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::WeakPtr<ConfigFileWatcher::Delegate> delegate_; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(ConfigFileWatcherImpl); 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ConfigFileWatcher::Delegate::~Delegate() { 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ConfigFileWatcher::ConfigFileWatcher( 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Delegate* delegate) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : impl_(new ConfigFileWatcherImpl(main_task_runner, 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) io_task_runner, delegate)) { 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ConfigFileWatcher::~ConfigFileWatcher() { 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) impl_->StopWatching(); 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) impl_ = NULL; 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ConfigFileWatcher::Watch(const base::FilePath& config_path) { 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) impl_->Watch(config_path); 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ConfigFileWatcherImpl::ConfigFileWatcherImpl( 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ConfigFileWatcher::Delegate* delegate) 1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) : retries_(0), 1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) delegate_weak_factory_(delegate), 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate_(delegate_weak_factory_.GetWeakPtr()), 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) main_task_runner_(main_task_runner), 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) io_task_runner_(io_task_runner) { 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(main_task_runner_->BelongsToCurrentThread()); 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ConfigFileWatcherImpl::Watch(const base::FilePath& config_path) { 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!io_task_runner_->BelongsToCurrentThread()) { 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) io_task_runner_->PostTask( 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FROM_HERE, 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&ConfigFileWatcherImpl::Watch, this, config_path)); 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(config_path_.empty()); 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(!config_updated_timer_); 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(!config_watcher_); 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create the timer that will be used for delayed-reading the configuration 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // file. 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) config_updated_timer_.reset(new base::DelayTimer<ConfigFileWatcherImpl>( 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FROM_HERE, base::TimeDelta::FromSeconds(2), this, 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &ConfigFileWatcherImpl::ReloadConfig)); 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Start watching the configuration file. 1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) config_watcher_.reset(new base::FilePathWatcher()); 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) config_path_ = config_path; 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!config_watcher_->Watch( 1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) config_path_, false, 1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::Bind(&ConfigFileWatcherImpl::OnConfigUpdated, this))) { 1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) PLOG(ERROR) << "Couldn't watch file '" << config_path_.value() << "'"; 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) main_task_runner_->PostTask( 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FROM_HERE, 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&ConfigFileWatcher::Delegate::OnConfigWatcherError, 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate_)); 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Force reloading of the configuration file at least once. 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ReloadConfig(); 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ConfigFileWatcherImpl::StopWatching() { 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(main_task_runner_->BelongsToCurrentThread()); 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate_weak_factory_.InvalidateWeakPtrs(); 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) io_task_runner_->PostTask( 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FROM_HERE, base::Bind(&ConfigFileWatcherImpl::FinishStopping, this)); 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ConfigFileWatcherImpl::~ConfigFileWatcherImpl() { 1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(!config_updated_timer_); 1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(!config_watcher_); 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ConfigFileWatcherImpl::FinishStopping() { 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(io_task_runner_->BelongsToCurrentThread()); 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) config_updated_timer_.reset(); 1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) config_watcher_.reset(); 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ConfigFileWatcherImpl::OnConfigUpdated(const base::FilePath& path, 1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool error) { 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(io_task_runner_->BelongsToCurrentThread()); 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Call ReloadConfig() after a short delay, so that we will not try to read 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the updated configuration file before it has been completely written. 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the writer moves the new configuration file into place atomically, 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // this delay may not be necessary. 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!error && config_path_ == path) 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) config_updated_timer_->Reset(); 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ConfigFileWatcherImpl::ReloadConfig() { 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(io_task_runner_->BelongsToCurrentThread()); 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string config; 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!file_util::ReadFileToString(config_path_, &config)) { 1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(OS_WIN) 1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // EACCESS may indicate a locking or sharing violation. Retry a few times 1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // before reporting an error. 1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (errno == EACCES && retries_ < kMaxRetries) { 1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) PLOG(WARNING) << "Failed to read '" << config_path_.value() << "'"; 1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) retries_ += 1; 1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) config_updated_timer_->Reset(); 1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif // defined(OS_WIN) 1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) PLOG(ERROR) << "Failed to read '" << config_path_.value() << "'"; 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) main_task_runner_->PostTask( 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FROM_HERE, 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&ConfigFileWatcher::Delegate::OnConfigWatcherError, 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate_)); 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) retries_ = 0; 2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Post an updated configuration only if it has actually changed. 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (config_ != config) { 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) config_ = config; 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) main_task_runner_->PostTask( 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FROM_HERE, 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&ConfigFileWatcher::Delegate::OnConfigUpdated, delegate_, 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) config_)); 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace remoting 217