| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/files/file_path_watcher.h" | 5 #include "base/files/file_path_watcher.h" |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 #include <string.h> | 9 #include <string.h> |
| 10 #include <sys/inotify.h> | 10 #include <sys/inotify.h> |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 81 | 81 |
| 82 // File descriptor returned by inotify_init. | 82 // File descriptor returned by inotify_init. |
| 83 const int inotify_fd_; | 83 const int inotify_fd_; |
| 84 | 84 |
| 85 // Flag set to true when startup was successful. | 85 // Flag set to true when startup was successful. |
| 86 bool valid_; | 86 bool valid_; |
| 87 | 87 |
| 88 DISALLOW_COPY_AND_ASSIGN(InotifyReader); | 88 DISALLOW_COPY_AND_ASSIGN(InotifyReader); |
| 89 }; | 89 }; |
| 90 | 90 |
| 91 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, | 91 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { |
| 92 public MessageLoop::DestructionObserver { | |
| 93 public: | 92 public: |
| 94 FilePathWatcherImpl(); | 93 FilePathWatcherImpl(); |
| 95 | 94 |
| 96 // Called for each event coming from the watch. |fired_watch| identifies the | 95 // Called for each event coming from the watch. |fired_watch| identifies the |
| 97 // watch that fired, |child| indicates what has changed, and is relative to | 96 // watch that fired, |child| indicates what has changed, and is relative to |
| 98 // the currently watched path for |fired_watch|. | 97 // the currently watched path for |fired_watch|. |
| 99 // | 98 // |
| 100 // |created| is true if the object appears. | 99 // |created| is true if the object appears. |
| 101 // |deleted| is true if the object disappears. | 100 // |deleted| is true if the object disappears. |
| 102 // |is_dir| is true if the object is a directory. | 101 // |is_dir| is true if the object is a directory. |
| 103 void OnFilePathChanged(InotifyReader::Watch fired_watch, | 102 void OnFilePathChanged(InotifyReader::Watch fired_watch, |
| 104 const FilePath::StringType& child, | 103 const FilePath::StringType& child, |
| 105 bool created, | 104 bool created, |
| 106 bool deleted, | 105 bool deleted, |
| 107 bool is_dir); | 106 bool is_dir); |
| 108 | 107 |
| 109 protected: | 108 protected: |
| 110 ~FilePathWatcherImpl() override {} | 109 ~FilePathWatcherImpl() override { |
| 110 in_destructor_ = true; |
| 111 CancelOnMessageLoopThreadOrInDestructor(); |
| 112 } |
| 111 | 113 |
| 112 private: | 114 private: |
| 113 // Start watching |path| for changes and notify |delegate| on each change. | 115 // Start watching |path| for changes and notify |delegate| on each change. |
| 114 // Returns true if watch for |path| has been added successfully. | 116 // Returns true if watch for |path| has been added successfully. |
| 115 bool Watch(const FilePath& path, | 117 bool Watch(const FilePath& path, |
| 116 bool recursive, | 118 bool recursive, |
| 117 const FilePathWatcher::Callback& callback) override; | 119 const FilePathWatcher::Callback& callback) override; |
| 118 | 120 |
| 119 // Cancel the watch. This unregisters the instance with InotifyReader. | 121 // Cancel the watch. This unregisters the instance with InotifyReader. |
| 120 void Cancel() override; | 122 void Cancel() override; |
| 121 | |
| 122 // Cleans up and stops observing the message_loop() thread. | |
| 123 void CancelOnMessageLoopThread() override; | 123 void CancelOnMessageLoopThread() override; |
| 124 | 124 void CancelOnMessageLoopThreadOrInDestructor(); |
| 125 // Deletion of the FilePathWatcher will call Cancel() to dispose of this | |
| 126 // object in the right thread. This also observes destruction of the required | |
| 127 // cleanup thread, in case it quits before Cancel() is called. | |
| 128 void WillDestroyCurrentMessageLoop() override; | |
| 129 | 125 |
| 130 // Inotify watches are installed for all directory components of |target_|. | 126 // Inotify watches are installed for all directory components of |target_|. |
| 131 // A WatchEntry instance holds: | 127 // A WatchEntry instance holds: |
| 132 // - |watch|: the watch descriptor for a component. | 128 // - |watch|: the watch descriptor for a component. |
| 133 // - |subdir|: the subdirectory that identifies the next component. | 129 // - |subdir|: the subdirectory that identifies the next component. |
| 134 // - For the last component, there is no next component, so it is empty. | 130 // - For the last component, there is no next component, so it is empty. |
| 135 // - |linkname|: the target of the symlink. | 131 // - |linkname|: the target of the symlink. |
| 136 // - Only if the target being watched is a symbolic link. | 132 // - Only if the target being watched is a symbolic link. |
| 137 struct WatchEntry { | 133 struct WatchEntry { |
| 138 explicit WatchEntry(const FilePath::StringType& dirname) | 134 explicit WatchEntry(const FilePath::StringType& dirname) |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 bool recursive_; | 179 bool recursive_; |
| 184 | 180 |
| 185 // The vector of watches and next component names for all path components, | 181 // The vector of watches and next component names for all path components, |
| 186 // starting at the root directory. The last entry corresponds to the watch for | 182 // starting at the root directory. The last entry corresponds to the watch for |
| 187 // |target_| and always stores an empty next component name in |subdir|. | 183 // |target_| and always stores an empty next component name in |subdir|. |
| 188 WatchVector watches_; | 184 WatchVector watches_; |
| 189 | 185 |
| 190 hash_map<InotifyReader::Watch, FilePath> recursive_paths_by_watch_; | 186 hash_map<InotifyReader::Watch, FilePath> recursive_paths_by_watch_; |
| 191 std::map<FilePath, InotifyReader::Watch> recursive_watches_by_path_; | 187 std::map<FilePath, InotifyReader::Watch> recursive_watches_by_path_; |
| 192 | 188 |
| 189 bool in_destructor_ = false; |
| 190 |
| 193 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); | 191 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); |
| 194 }; | 192 }; |
| 195 | 193 |
| 196 void InotifyReaderCallback(InotifyReader* reader, int inotify_fd) { | 194 void InotifyReaderCallback(InotifyReader* reader, int inotify_fd) { |
| 197 // Make sure the file descriptors are good for use with select(). | 195 // Make sure the file descriptors are good for use with select(). |
| 198 CHECK_LE(0, inotify_fd); | 196 CHECK_LE(0, inotify_fd); |
| 199 CHECK_GT(FD_SETSIZE, inotify_fd); | 197 CHECK_GT(FD_SETSIZE, inotify_fd); |
| 200 | 198 |
| 201 trace_event::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop(); | 199 trace_event::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop(); |
| 202 | 200 |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 422 bool FilePathWatcherImpl::Watch(const FilePath& path, | 420 bool FilePathWatcherImpl::Watch(const FilePath& path, |
| 423 bool recursive, | 421 bool recursive, |
| 424 const FilePathWatcher::Callback& callback) { | 422 const FilePathWatcher::Callback& callback) { |
| 425 DCHECK(target_.empty()); | 423 DCHECK(target_.empty()); |
| 426 DCHECK(MessageLoopForIO::current()); | 424 DCHECK(MessageLoopForIO::current()); |
| 427 | 425 |
| 428 set_task_runner(ThreadTaskRunnerHandle::Get()); | 426 set_task_runner(ThreadTaskRunnerHandle::Get()); |
| 429 callback_ = callback; | 427 callback_ = callback; |
| 430 target_ = path; | 428 target_ = path; |
| 431 recursive_ = recursive; | 429 recursive_ = recursive; |
| 432 MessageLoop::current()->AddDestructionObserver(this); | |
| 433 | 430 |
| 434 std::vector<FilePath::StringType> comps; | 431 std::vector<FilePath::StringType> comps; |
| 435 target_.GetComponents(&comps); | 432 target_.GetComponents(&comps); |
| 436 DCHECK(!comps.empty()); | 433 DCHECK(!comps.empty()); |
| 437 for (size_t i = 1; i < comps.size(); ++i) | 434 for (size_t i = 1; i < comps.size(); ++i) |
| 438 watches_.push_back(WatchEntry(comps[i])); | 435 watches_.push_back(WatchEntry(comps[i])); |
| 439 watches_.push_back(WatchEntry(FilePath::StringType())); | 436 watches_.push_back(WatchEntry(FilePath::StringType())); |
| 440 UpdateWatches(); | 437 UpdateWatches(); |
| 441 return true; | 438 return true; |
| 442 } | 439 } |
| 443 | 440 |
| 444 void FilePathWatcherImpl::Cancel() { | 441 void FilePathWatcherImpl::Cancel() { |
| 445 if (callback_.is_null()) { | 442 if (callback_.is_null()) { |
| 446 // Watch was never called, or the message_loop() thread is already gone. | 443 // Watch was never called, or the message_loop() thread is already gone. |
| 447 set_cancelled(); | 444 set_cancelled(); |
| 448 return; | 445 return; |
| 449 } | 446 } |
| 450 | 447 |
| 451 // Switch to the message_loop() if necessary so we can access |watches_|. | 448 // Switch to the message_loop() if necessary so we can access |watches_|. |
| 452 if (!task_runner()->BelongsToCurrentThread()) { | 449 if (!task_runner()->BelongsToCurrentThread()) { |
| 453 task_runner()->PostTask(FROM_HERE, Bind(&FilePathWatcher::CancelWatch, | 450 task_runner()->PostTask(FROM_HERE, Bind(&FilePathWatcher::CancelWatch, |
| 454 make_scoped_refptr(this))); | 451 make_scoped_refptr(this))); |
| 455 } else { | 452 } else { |
| 456 CancelOnMessageLoopThread(); | 453 CancelOnMessageLoopThread(); |
| 457 } | 454 } |
| 458 } | 455 } |
| 459 | 456 |
| 460 void FilePathWatcherImpl::CancelOnMessageLoopThread() { | 457 void FilePathWatcherImpl::CancelOnMessageLoopThread() { |
| 461 DCHECK(task_runner()->BelongsToCurrentThread()); | 458 CancelOnMessageLoopThreadOrInDestructor(); |
| 459 } |
| 460 |
| 461 void FilePathWatcherImpl::CancelOnMessageLoopThreadOrInDestructor() { |
| 462 DCHECK(in_destructor_ || task_runner()->BelongsToCurrentThread()); |
| 463 |
| 464 if (is_cancelled()) |
| 465 return; |
| 466 |
| 462 set_cancelled(); | 467 set_cancelled(); |
| 463 | 468 |
| 464 if (!callback_.is_null()) { | 469 if (!callback_.is_null()) |
| 465 MessageLoop::current()->RemoveDestructionObserver(this); | |
| 466 callback_.Reset(); | 470 callback_.Reset(); |
| 467 } | |
| 468 | 471 |
| 469 for (size_t i = 0; i < watches_.size(); ++i) | 472 for (size_t i = 0; i < watches_.size(); ++i) |
| 470 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); | 473 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); |
| 471 watches_.clear(); | 474 watches_.clear(); |
| 472 target_.clear(); | 475 target_.clear(); |
| 473 | 476 |
| 474 if (recursive_) | 477 if (recursive_) |
| 475 RemoveRecursiveWatches(); | 478 RemoveRecursiveWatches(); |
| 476 } | 479 } |
| 477 | 480 |
| 478 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { | |
| 479 CancelOnMessageLoopThread(); | |
| 480 } | |
| 481 | |
| 482 void FilePathWatcherImpl::UpdateWatches() { | 481 void FilePathWatcherImpl::UpdateWatches() { |
| 483 // Ensure this runs on the message_loop() exclusively in order to avoid | 482 // Ensure this runs on the message_loop() exclusively in order to avoid |
| 484 // concurrency issues. | 483 // concurrency issues. |
| 485 DCHECK(task_runner()->BelongsToCurrentThread()); | 484 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 486 DCHECK(HasValidWatchVector()); | 485 DCHECK(HasValidWatchVector()); |
| 487 | 486 |
| 488 // Walk the list of watches and update them as we go. | 487 // Walk the list of watches and update them as we go. |
| 489 FilePath path(FILE_PATH_LITERAL("/")); | 488 FilePath path(FILE_PATH_LITERAL("/")); |
| 490 for (size_t i = 0; i < watches_.size(); ++i) { | 489 for (size_t i = 0; i < watches_.size(); ++i) { |
| 491 WatchEntry& watch_entry = watches_[i]; | 490 WatchEntry& watch_entry = watches_[i]; |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 652 return watches_.back().subdir.empty(); | 651 return watches_.back().subdir.empty(); |
| 653 } | 652 } |
| 654 | 653 |
| 655 } // namespace | 654 } // namespace |
| 656 | 655 |
| 657 FilePathWatcher::FilePathWatcher() { | 656 FilePathWatcher::FilePathWatcher() { |
| 658 impl_ = new FilePathWatcherImpl(); | 657 impl_ = new FilePathWatcherImpl(); |
| 659 } | 658 } |
| 660 | 659 |
| 661 } // namespace base | 660 } // namespace base |
| OLD | NEW |