Index: base/files/file_path_watcher_win.cc |
diff --git a/base/files/file_path_watcher_win.cc b/base/files/file_path_watcher_win.cc |
deleted file mode 100644 |
index 081698f8df12daed09679dfa880445cd370c9ea5..0000000000000000000000000000000000000000 |
--- a/base/files/file_path_watcher_win.cc |
+++ /dev/null |
@@ -1,300 +0,0 @@ |
-// Copyright (c) 2011 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 "base/files/file_path_watcher.h" |
- |
-#include "base/bind.h" |
-#include "base/files/file.h" |
-#include "base/files/file_path.h" |
-#include "base/files/file_util.h" |
-#include "base/logging.h" |
-#include "base/memory/ref_counted.h" |
-#include "base/thread_task_runner_handle.h" |
-#include "base/time/time.h" |
-#include "base/win/object_watcher.h" |
- |
-namespace base { |
- |
-namespace { |
- |
-class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, |
- public base::win::ObjectWatcher::Delegate, |
- public MessageLoop::DestructionObserver { |
- public: |
- FilePathWatcherImpl() |
- : handle_(INVALID_HANDLE_VALUE), |
- recursive_watch_(false) {} |
- |
- // FilePathWatcher::PlatformDelegate overrides. |
- bool Watch(const FilePath& path, |
- bool recursive, |
- const FilePathWatcher::Callback& callback) override; |
- void Cancel() override; |
- |
- // Deletion of the FilePathWatcher will call Cancel() to dispose of this |
- // object in the right thread. This also observes destruction of the required |
- // cleanup thread, in case it quits before Cancel() is called. |
- void WillDestroyCurrentMessageLoop() override; |
- |
- // Callback from MessageLoopForIO. |
- void OnObjectSignaled(HANDLE object) override; |
- |
- private: |
- ~FilePathWatcherImpl() override {} |
- |
- // Setup a watch handle for directory |dir|. Set |recursive| to true to watch |
- // the directory sub trees. Returns true if no fatal error occurs. |handle| |
- // will receive the handle value if |dir| is watchable, otherwise |
- // INVALID_HANDLE_VALUE. |
- static bool SetupWatchHandle(const FilePath& dir, |
- bool recursive, |
- HANDLE* handle) WARN_UNUSED_RESULT; |
- |
- // (Re-)Initialize the watch handle. |
- bool UpdateWatch() WARN_UNUSED_RESULT; |
- |
- // Destroy the watch handle. |
- void DestroyWatch(); |
- |
- // Cleans up and stops observing the |task_runner_| thread. |
- void CancelOnMessageLoopThread() override; |
- |
- // Callback to notify upon changes. |
- FilePathWatcher::Callback callback_; |
- |
- // Path we're supposed to watch (passed to callback). |
- FilePath target_; |
- |
- // Handle for FindFirstChangeNotification. |
- HANDLE handle_; |
- |
- // ObjectWatcher to watch handle_ for events. |
- base::win::ObjectWatcher watcher_; |
- |
- // Set to true to watch the sub trees of the specified directory file path. |
- bool recursive_watch_; |
- |
- // Keep track of the last modified time of the file. We use nulltime |
- // to represent the file not existing. |
- Time last_modified_; |
- |
- // The time at which we processed the first notification with the |
- // |last_modified_| time stamp. |
- Time first_notification_; |
- |
- DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); |
-}; |
- |
-bool FilePathWatcherImpl::Watch(const FilePath& path, |
- bool recursive, |
- const FilePathWatcher::Callback& callback) { |
- DCHECK(target_.value().empty()); // Can only watch one path. |
- |
- set_task_runner(ThreadTaskRunnerHandle::Get()); |
- callback_ = callback; |
- target_ = path; |
- recursive_watch_ = recursive; |
- MessageLoop::current()->AddDestructionObserver(this); |
- |
- File::Info file_info; |
- if (GetFileInfo(target_, &file_info)) { |
- last_modified_ = file_info.last_modified; |
- first_notification_ = Time::Now(); |
- } |
- |
- if (!UpdateWatch()) |
- return false; |
- |
- watcher_.StartWatching(handle_, this); |
- |
- return true; |
-} |
- |
-void FilePathWatcherImpl::Cancel() { |
- if (callback_.is_null()) { |
- // Watch was never called, or the |task_runner_| has already quit. |
- set_cancelled(); |
- return; |
- } |
- |
- // Switch to the file thread if necessary so we can stop |watcher_|. |
- if (!task_runner()->BelongsToCurrentThread()) { |
- task_runner()->PostTask(FROM_HERE, Bind(&FilePathWatcher::CancelWatch, |
- make_scoped_refptr(this))); |
- } else { |
- CancelOnMessageLoopThread(); |
- } |
-} |
- |
-void FilePathWatcherImpl::CancelOnMessageLoopThread() { |
- DCHECK(task_runner()->BelongsToCurrentThread()); |
- set_cancelled(); |
- |
- if (handle_ != INVALID_HANDLE_VALUE) |
- DestroyWatch(); |
- |
- if (!callback_.is_null()) { |
- MessageLoop::current()->RemoveDestructionObserver(this); |
- callback_.Reset(); |
- } |
-} |
- |
-void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { |
- CancelOnMessageLoopThread(); |
-} |
- |
-void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) { |
- DCHECK(object == handle_); |
- // Make sure we stay alive through the body of this function. |
- scoped_refptr<FilePathWatcherImpl> keep_alive(this); |
- |
- if (!UpdateWatch()) { |
- callback_.Run(target_, true /* error */); |
- return; |
- } |
- |
- // Check whether the event applies to |target_| and notify the callback. |
- File::Info file_info; |
- bool file_exists = GetFileInfo(target_, &file_info); |
- if (recursive_watch_) { |
- // Only the mtime of |target_| is tracked but in a recursive watch, |
- // some other file or directory may have changed so all notifications |
- // are passed through. It is possible to figure out which file changed |
- // using ReadDirectoryChangesW() instead of FindFirstChangeNotification(), |
- // but that function is quite complicated: |
- // http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html |
- callback_.Run(target_, false); |
- } else if (file_exists && (last_modified_.is_null() || |
- last_modified_ != file_info.last_modified)) { |
- last_modified_ = file_info.last_modified; |
- first_notification_ = Time::Now(); |
- callback_.Run(target_, false); |
- } else if (file_exists && last_modified_ == file_info.last_modified && |
- !first_notification_.is_null()) { |
- // The target's last modification time is equal to what's on record. This |
- // means that either an unrelated event occurred, or the target changed |
- // again (file modification times only have a resolution of 1s). Comparing |
- // file modification times against the wall clock is not reliable to find |
- // out whether the change is recent, since this code might just run too |
- // late. Moreover, there's no guarantee that file modification time and wall |
- // clock times come from the same source. |
- // |
- // Instead, the time at which the first notification carrying the current |
- // |last_notified_| time stamp is recorded. Later notifications that find |
- // the same file modification time only need to be forwarded until wall |
- // clock has advanced one second from the initial notification. After that |
- // interval, client code is guaranteed to having seen the current revision |
- // of the file. |
- if (Time::Now() - first_notification_ > TimeDelta::FromSeconds(1)) { |
- // Stop further notifications for this |last_modification_| time stamp. |
- first_notification_ = Time(); |
- } |
- callback_.Run(target_, false); |
- } else if (!file_exists && !last_modified_.is_null()) { |
- last_modified_ = Time(); |
- callback_.Run(target_, false); |
- } |
- |
- // The watch may have been cancelled by the callback. |
- if (handle_ != INVALID_HANDLE_VALUE) |
- watcher_.StartWatching(handle_, this); |
-} |
- |
-// static |
-bool FilePathWatcherImpl::SetupWatchHandle(const FilePath& dir, |
- bool recursive, |
- HANDLE* handle) { |
- *handle = FindFirstChangeNotification( |
- dir.value().c_str(), |
- recursive, |
- FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE | |
- FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | |
- FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY); |
- if (*handle != INVALID_HANDLE_VALUE) { |
- // Make sure the handle we got points to an existing directory. It seems |
- // that windows sometimes hands out watches to directories that are |
- // about to go away, but doesn't sent notifications if that happens. |
- if (!DirectoryExists(dir)) { |
- FindCloseChangeNotification(*handle); |
- *handle = INVALID_HANDLE_VALUE; |
- } |
- return true; |
- } |
- |
- // If FindFirstChangeNotification failed because the target directory |
- // doesn't exist, access is denied (happens if the file is already gone but |
- // there are still handles open), or the target is not a directory, try the |
- // immediate parent directory instead. |
- DWORD error_code = GetLastError(); |
- if (error_code != ERROR_FILE_NOT_FOUND && |
- error_code != ERROR_PATH_NOT_FOUND && |
- error_code != ERROR_ACCESS_DENIED && |
- error_code != ERROR_SHARING_VIOLATION && |
- error_code != ERROR_DIRECTORY) { |
- DPLOG(ERROR) << "FindFirstChangeNotification failed for " |
- << dir.value(); |
- return false; |
- } |
- |
- return true; |
-} |
- |
-bool FilePathWatcherImpl::UpdateWatch() { |
- if (handle_ != INVALID_HANDLE_VALUE) |
- DestroyWatch(); |
- |
- // Start at the target and walk up the directory chain until we succesfully |
- // create a watch handle in |handle_|. |child_dirs| keeps a stack of child |
- // directories stripped from target, in reverse order. |
- std::vector<FilePath> child_dirs; |
- FilePath watched_path(target_); |
- while (true) { |
- if (!SetupWatchHandle(watched_path, recursive_watch_, &handle_)) |
- return false; |
- |
- // Break if a valid handle is returned. Try the parent directory otherwise. |
- if (handle_ != INVALID_HANDLE_VALUE) |
- break; |
- |
- // Abort if we hit the root directory. |
- child_dirs.push_back(watched_path.BaseName()); |
- FilePath parent(watched_path.DirName()); |
- if (parent == watched_path) { |
- DLOG(ERROR) << "Reached the root directory"; |
- return false; |
- } |
- watched_path = parent; |
- } |
- |
- // At this point, handle_ is valid. However, the bottom-up search that the |
- // above code performs races against directory creation. So try to walk back |
- // down and see whether any children appeared in the mean time. |
- while (!child_dirs.empty()) { |
- watched_path = watched_path.Append(child_dirs.back()); |
- child_dirs.pop_back(); |
- HANDLE temp_handle = INVALID_HANDLE_VALUE; |
- if (!SetupWatchHandle(watched_path, recursive_watch_, &temp_handle)) |
- return false; |
- if (temp_handle == INVALID_HANDLE_VALUE) |
- break; |
- FindCloseChangeNotification(handle_); |
- handle_ = temp_handle; |
- } |
- |
- return true; |
-} |
- |
-void FilePathWatcherImpl::DestroyWatch() { |
- watcher_.StopWatching(); |
- FindCloseChangeNotification(handle_); |
- handle_ = INVALID_HANDLE_VALUE; |
-} |
- |
-} // namespace |
- |
-FilePathWatcher::FilePathWatcher() { |
- impl_ = new FilePathWatcherImpl(); |
-} |
- |
-} // namespace base |