Chromium Code Reviews| 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 10 matching lines...) Expand all Loading... | |
| 21 | 21 |
| 22 #include "base/bind.h" | 22 #include "base/bind.h" |
| 23 #include "base/containers/hash_tables.h" | 23 #include "base/containers/hash_tables.h" |
| 24 #include "base/files/file_enumerator.h" | 24 #include "base/files/file_enumerator.h" |
| 25 #include "base/files/file_path.h" | 25 #include "base/files/file_path.h" |
| 26 #include "base/files/file_util.h" | 26 #include "base/files/file_util.h" |
| 27 #include "base/lazy_instance.h" | 27 #include "base/lazy_instance.h" |
| 28 #include "base/location.h" | 28 #include "base/location.h" |
| 29 #include "base/logging.h" | 29 #include "base/logging.h" |
| 30 #include "base/macros.h" | 30 #include "base/macros.h" |
| 31 #include "base/memory/weak_ptr.h" | |
| 31 #include "base/posix/eintr_wrapper.h" | 32 #include "base/posix/eintr_wrapper.h" |
| 32 #include "base/single_thread_task_runner.h" | 33 #include "base/single_thread_task_runner.h" |
| 33 #include "base/stl_util.h" | 34 #include "base/stl_util.h" |
| 34 #include "base/synchronization/lock.h" | 35 #include "base/synchronization/lock.h" |
| 35 #include "base/threading/thread.h" | 36 #include "base/threading/thread.h" |
| 36 #include "base/threading/thread_task_runner_handle.h" | 37 #include "base/threading/thread_task_runner_handle.h" |
| 37 #include "base/trace_event/trace_event.h" | 38 #include "base/trace_event/trace_event.h" |
| 38 | 39 |
| 39 namespace base { | 40 namespace base { |
| 40 | 41 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 99 // |created| is true if the object appears. | 100 // |created| is true if the object appears. |
| 100 // |deleted| is true if the object disappears. | 101 // |deleted| is true if the object disappears. |
| 101 // |is_dir| is true if the object is a directory. | 102 // |is_dir| is true if the object is a directory. |
| 102 void OnFilePathChanged(InotifyReader::Watch fired_watch, | 103 void OnFilePathChanged(InotifyReader::Watch fired_watch, |
| 103 const FilePath::StringType& child, | 104 const FilePath::StringType& child, |
| 104 bool created, | 105 bool created, |
| 105 bool deleted, | 106 bool deleted, |
| 106 bool is_dir); | 107 bool is_dir); |
| 107 | 108 |
| 108 protected: | 109 protected: |
| 109 ~FilePathWatcherImpl() override { | 110 ~FilePathWatcherImpl() override = default; |
| 110 in_destructor_ = true; | |
| 111 CancelOnMessageLoopThreadOrInDestructor(); | |
| 112 } | |
| 113 | 111 |
| 114 private: | 112 private: |
| 113 void OnFilePathChangedOnOriginSequence(InotifyReader::Watch fired_watch, | |
| 114 const FilePath::StringType& child, | |
| 115 bool created, | |
| 116 bool deleted, | |
| 117 bool is_dir); | |
| 118 | |
| 115 // Start watching |path| for changes and notify |delegate| on each change. | 119 // Start watching |path| for changes and notify |delegate| on each change. |
| 116 // Returns true if watch for |path| has been added successfully. | 120 // Returns true if watch for |path| has been added successfully. |
| 117 bool Watch(const FilePath& path, | 121 bool Watch(const FilePath& path, |
| 118 bool recursive, | 122 bool recursive, |
| 119 const FilePathWatcher::Callback& callback) override; | 123 const FilePathWatcher::Callback& callback) override; |
| 120 | 124 |
| 121 // Cancel the watch. This unregisters the instance with InotifyReader. | 125 // Cancel the watch. This unregisters the instance with InotifyReader. |
| 122 void Cancel() override; | 126 void Cancel() override; |
| 123 void CancelOnMessageLoopThreadOrInDestructor(); | |
| 124 | 127 |
| 125 // Inotify watches are installed for all directory components of |target_|. | 128 // Inotify watches are installed for all directory components of |target_|. |
| 126 // A WatchEntry instance holds: | 129 // A WatchEntry instance holds: |
| 127 // - |watch|: the watch descriptor for a component. | 130 // - |watch|: the watch descriptor for a component. |
| 128 // - |subdir|: the subdirectory that identifies the next component. | 131 // - |subdir|: the subdirectory that identifies the next component. |
| 129 // - For the last component, there is no next component, so it is empty. | 132 // - For the last component, there is no next component, so it is empty. |
| 130 // - |linkname|: the target of the symlink. | 133 // - |linkname|: the target of the symlink. |
| 131 // - Only if the target being watched is a symbolic link. | 134 // - Only if the target being watched is a symbolic link. |
| 132 struct WatchEntry { | 135 struct WatchEntry { |
| 133 explicit WatchEntry(const FilePath::StringType& dirname) | 136 explicit WatchEntry(const FilePath::StringType& dirname) |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 178 bool recursive_; | 181 bool recursive_; |
| 179 | 182 |
| 180 // The vector of watches and next component names for all path components, | 183 // The vector of watches and next component names for all path components, |
| 181 // starting at the root directory. The last entry corresponds to the watch for | 184 // starting at the root directory. The last entry corresponds to the watch for |
| 182 // |target_| and always stores an empty next component name in |subdir|. | 185 // |target_| and always stores an empty next component name in |subdir|. |
| 183 WatchVector watches_; | 186 WatchVector watches_; |
| 184 | 187 |
| 185 hash_map<InotifyReader::Watch, FilePath> recursive_paths_by_watch_; | 188 hash_map<InotifyReader::Watch, FilePath> recursive_paths_by_watch_; |
| 186 std::map<FilePath, InotifyReader::Watch> recursive_watches_by_path_; | 189 std::map<FilePath, InotifyReader::Watch> recursive_watches_by_path_; |
| 187 | 190 |
| 188 bool in_destructor_ = false; | 191 WeakPtrFactory<FilePathWatcherImpl> weak_factory_; |
| 189 | 192 |
| 190 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); | 193 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); |
| 191 }; | 194 }; |
| 192 | 195 |
| 193 void InotifyReaderCallback(InotifyReader* reader, int inotify_fd) { | 196 void InotifyReaderCallback(InotifyReader* reader, int inotify_fd) { |
| 194 // Make sure the file descriptors are good for use with select(). | 197 // Make sure the file descriptors are good for use with select(). |
| 195 CHECK_LE(0, inotify_fd); | 198 CHECK_LE(0, inotify_fd); |
| 196 CHECK_GT(FD_SETSIZE, inotify_fd); | 199 CHECK_GT(FD_SETSIZE, inotify_fd); |
| 197 | 200 |
| 198 trace_event::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop(); | 201 trace_event::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop(); |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 305 ++watcher) { | 308 ++watcher) { |
| 306 (*watcher)->OnFilePathChanged(event->wd, | 309 (*watcher)->OnFilePathChanged(event->wd, |
| 307 child, | 310 child, |
| 308 event->mask & (IN_CREATE | IN_MOVED_TO), | 311 event->mask & (IN_CREATE | IN_MOVED_TO), |
| 309 event->mask & (IN_DELETE | IN_MOVED_FROM), | 312 event->mask & (IN_DELETE | IN_MOVED_FROM), |
| 310 event->mask & IN_ISDIR); | 313 event->mask & IN_ISDIR); |
| 311 } | 314 } |
| 312 } | 315 } |
| 313 | 316 |
| 314 FilePathWatcherImpl::FilePathWatcherImpl() | 317 FilePathWatcherImpl::FilePathWatcherImpl() |
| 315 : recursive_(false) { | 318 : recursive_(false), weak_factory_(this) {} |
| 316 } | |
| 317 | 319 |
| 318 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, | 320 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, |
| 319 const FilePath::StringType& child, | 321 const FilePath::StringType& child, |
| 320 bool created, | 322 bool created, |
| 321 bool deleted, | 323 bool deleted, |
| 322 bool is_dir) { | 324 bool is_dir) { |
| 323 if (!task_runner()->BelongsToCurrentThread()) { | 325 // This method is invoked on the Inotify thread. Switch to task_runner() to |
|
gab
2016/12/23 16:05:06
DCHECK not on TaskRunner thread?
fdoray
2016/12/23 21:16:07
Done.
| |
| 324 // Switch to task_runner() to access |watches_| safely. | 326 // access |watches_| safely. Use a WeakPtr to prevent the callback from |
| 325 task_runner()->PostTask(FROM_HERE, | 327 // running after the destructor is invoked (i.e. after the watch is |
| 326 Bind(&FilePathWatcherImpl::OnFilePathChanged, this, | 328 // cancelled). |
|
fdoray
2016/12/23 15:34:45
Cancel() is only called from ~FilePathWatcher http
gab
2016/12/23 16:05:06
Ah cool now I see why you'd made all those changes
fdoray
2016/12/23 21:16:07
Done.
| |
| 327 fired_watch, child, created, deleted, is_dir)); | 329 task_runner()->PostTask( |
| 328 return; | 330 FROM_HERE, Bind(&FilePathWatcherImpl::OnFilePathChangedOnOriginSequence, |
| 329 } | 331 weak_factory_.GetWeakPtr(), fired_watch, child, created, |
|
gab
2016/12/23 16:05:06
It's not safe to vend WeakPtr from one sequence an
fdoray
2016/12/23 21:16:07
Actually, the WeakPtrs of a given WeakPtrFactory a
gab
2017/01/05 21:22:53
Good point, and it's implicit that FilePathWatcher
| |
| 332 deleted, is_dir)); | |
| 333 } | |
| 330 | 334 |
| 331 // Check to see if CancelOnMessageLoopThreadOrInDestructor() has already been | 335 void FilePathWatcherImpl::OnFilePathChangedOnOriginSequence( |
| 332 // called. May happen when code flow reaches here from the PostTask() above. | 336 InotifyReader::Watch fired_watch, |
| 333 if (watches_.empty()) { | 337 const FilePath::StringType& child, |
| 334 DCHECK(target_.empty()); | 338 bool created, |
| 335 return; | 339 bool deleted, |
| 336 } | 340 bool is_dir) { |
| 337 | |
| 338 DCHECK(task_runner()->BelongsToCurrentThread()); | 341 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 342 DCHECK(!watches_.empty()); | |
| 339 DCHECK(HasValidWatchVector()); | 343 DCHECK(HasValidWatchVector()); |
| 340 | 344 |
| 341 // Used below to avoid multiple recursive updates. | 345 // Used below to avoid multiple recursive updates. |
| 342 bool did_update = false; | 346 bool did_update = false; |
| 343 | 347 |
| 344 // Find the entry in |watches_| that corresponds to |fired_watch|. | 348 // Find the entry in |watches_| that corresponds to |fired_watch|. |
| 345 for (size_t i = 0; i < watches_.size(); ++i) { | 349 for (size_t i = 0; i < watches_.size(); ++i) { |
| 346 const WatchEntry& watch_entry = watches_[i]; | 350 const WatchEntry& watch_entry = watches_[i]; |
| 347 if (fired_watch != watch_entry.watch) | 351 if (fired_watch != watch_entry.watch) |
| 348 continue; | 352 continue; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 430 target_.GetComponents(&comps); | 434 target_.GetComponents(&comps); |
| 431 DCHECK(!comps.empty()); | 435 DCHECK(!comps.empty()); |
| 432 for (size_t i = 1; i < comps.size(); ++i) | 436 for (size_t i = 1; i < comps.size(); ++i) |
| 433 watches_.push_back(WatchEntry(comps[i])); | 437 watches_.push_back(WatchEntry(comps[i])); |
| 434 watches_.push_back(WatchEntry(FilePath::StringType())); | 438 watches_.push_back(WatchEntry(FilePath::StringType())); |
| 435 UpdateWatches(); | 439 UpdateWatches(); |
| 436 return true; | 440 return true; |
| 437 } | 441 } |
| 438 | 442 |
| 439 void FilePathWatcherImpl::Cancel() { | 443 void FilePathWatcherImpl::Cancel() { |
| 440 if (callback_.is_null()) { | 444 if (!callback_) { |
| 441 // Watch was never called, or the message_loop() thread is already gone. | 445 // Watch() was never called. |
| 442 set_cancelled(); | 446 set_cancelled(); |
| 443 return; | 447 return; |
| 444 } | 448 } |
| 445 | 449 |
| 446 // Switch to the task_runner() if necessary so we can access |watches_|. | 450 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 447 if (!task_runner()->BelongsToCurrentThread()) { | 451 DCHECK(!is_cancelled()); |
| 448 task_runner()->PostTask( | |
| 449 FROM_HERE, | |
| 450 Bind(&FilePathWatcherImpl::CancelOnMessageLoopThreadOrInDestructor, | |
| 451 this)); | |
| 452 } else { | |
| 453 CancelOnMessageLoopThreadOrInDestructor(); | |
| 454 } | |
| 455 } | |
| 456 | |
| 457 void FilePathWatcherImpl::CancelOnMessageLoopThreadOrInDestructor() { | |
| 458 DCHECK(in_destructor_ || task_runner()->BelongsToCurrentThread()); | |
| 459 | |
| 460 if (is_cancelled()) | |
| 461 return; | |
| 462 | 452 |
| 463 set_cancelled(); | 453 set_cancelled(); |
| 464 | 454 callback_.Reset(); |
| 465 if (!callback_.is_null()) | |
| 466 callback_.Reset(); | |
| 467 | 455 |
| 468 for (size_t i = 0; i < watches_.size(); ++i) | 456 for (size_t i = 0; i < watches_.size(); ++i) |
| 469 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); | 457 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); |
| 470 watches_.clear(); | 458 watches_.clear(); |
| 471 target_.clear(); | 459 target_.clear(); |
| 472 | 460 RemoveRecursiveWatches(); |
| 473 if (recursive_) | |
| 474 RemoveRecursiveWatches(); | |
| 475 } | 461 } |
| 476 | 462 |
| 477 void FilePathWatcherImpl::UpdateWatches() { | 463 void FilePathWatcherImpl::UpdateWatches() { |
| 478 // Ensure this runs on the task_runner() exclusively in order to avoid | 464 // Ensure this runs on the task_runner() exclusively in order to avoid |
| 479 // concurrency issues. | 465 // concurrency issues. |
| 480 DCHECK(task_runner()->BelongsToCurrentThread()); | 466 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 481 DCHECK(HasValidWatchVector()); | 467 DCHECK(HasValidWatchVector()); |
| 482 | 468 |
| 483 // Walk the list of watches and update them as we go. | 469 // Walk the list of watches and update them as we go. |
| 484 FilePath path(FILE_PATH_LITERAL("/")); | 470 FilePath path(FILE_PATH_LITERAL("/")); |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 654 } | 640 } |
| 655 | 641 |
| 656 } // namespace | 642 } // namespace |
| 657 | 643 |
| 658 FilePathWatcher::FilePathWatcher() { | 644 FilePathWatcher::FilePathWatcher() { |
| 659 sequence_checker_.DetachFromSequence(); | 645 sequence_checker_.DetachFromSequence(); |
| 660 impl_ = new FilePathWatcherImpl(); | 646 impl_ = new FilePathWatcherImpl(); |
| 661 } | 647 } |
| 662 | 648 |
| 663 } // namespace base | 649 } // namespace base |
| OLD | NEW |