Index: base/files/file_path_watcher_linux.cc |
diff --git a/base/files/file_path_watcher_linux.cc b/base/files/file_path_watcher_linux.cc |
index d060e49136cbc88dc095b5872d148607af501d39..9589e9b788dce7807ac0b97a011e6012e6b47860 100644 |
--- a/base/files/file_path_watcher_linux.cc |
+++ b/base/files/file_path_watcher_linux.cc |
@@ -28,6 +28,8 @@ |
#include "base/location.h" |
#include "base/logging.h" |
#include "base/macros.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/memory/weak_ptr.h" |
#include "base/posix/eintr_wrapper.h" |
#include "base/single_thread_task_runner.h" |
#include "base/stl_util.h" |
@@ -91,6 +93,7 @@ class InotifyReader { |
class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { |
public: |
FilePathWatcherImpl(); |
+ ~FilePathWatcherImpl() override; |
// Called for each event coming from the watch. |fired_watch| identifies the |
// watch that fired, |child| indicates what has changed, and is relative to |
@@ -105,13 +108,13 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { |
bool deleted, |
bool is_dir); |
- protected: |
- ~FilePathWatcherImpl() override { |
- in_destructor_ = true; |
- CancelOnMessageLoopThreadOrInDestructor(); |
- } |
- |
private: |
+ void OnFilePathChangedOnOriginSequence(InotifyReader::Watch fired_watch, |
+ const FilePath::StringType& child, |
+ bool created, |
+ bool deleted, |
+ bool is_dir); |
+ |
// Start watching |path| for changes and notify |delegate| on each change. |
// Returns true if watch for |path| has been added successfully. |
bool Watch(const FilePath& path, |
@@ -120,7 +123,6 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { |
// Cancel the watch. This unregisters the instance with InotifyReader. |
void Cancel() override; |
- void CancelOnMessageLoopThreadOrInDestructor(); |
// Inotify watches are installed for all directory components of |target_|. |
// A WatchEntry instance holds: |
@@ -185,7 +187,7 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { |
hash_map<InotifyReader::Watch, FilePath> recursive_paths_by_watch_; |
std::map<FilePath, InotifyReader::Watch> recursive_watches_by_path_; |
- bool in_destructor_ = false; |
+ WeakPtrFactory<FilePathWatcherImpl> weak_factory_; |
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); |
}; |
@@ -312,7 +314,10 @@ void InotifyReader::OnInotifyEvent(const inotify_event* event) { |
} |
FilePathWatcherImpl::FilePathWatcherImpl() |
- : recursive_(false) { |
+ : recursive_(false), weak_factory_(this) {} |
+ |
+FilePathWatcherImpl::~FilePathWatcherImpl() { |
+ DCHECK(!task_runner() || task_runner()->RunsTasksOnCurrentThread()); |
} |
void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, |
@@ -320,22 +325,25 @@ void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, |
bool created, |
bool deleted, |
bool is_dir) { |
- if (!task_runner()->RunsTasksOnCurrentThread()) { |
- // Switch to task_runner() to access |watches_| safely. |
- task_runner()->PostTask(FROM_HERE, |
- Bind(&FilePathWatcherImpl::OnFilePathChanged, this, |
- fired_watch, child, created, deleted, is_dir)); |
- return; |
- } |
- |
- // Check to see if CancelOnMessageLoopThreadOrInDestructor() has already been |
- // called. May happen when code flow reaches here from the PostTask() above. |
- if (watches_.empty()) { |
- DCHECK(target_.empty()); |
- return; |
- } |
+ DCHECK(!task_runner()->RunsTasksOnCurrentThread()); |
+ |
+ // This method is invoked on the Inotify thread. Switch to task_runner() to |
+ // access |watches_| safely. Use a WeakPtr to prevent the callback from |
+ // running after |this| is destroyed (i.e. after the watch is cancelled). |
+ task_runner()->PostTask( |
+ FROM_HERE, Bind(&FilePathWatcherImpl::OnFilePathChangedOnOriginSequence, |
+ weak_factory_.GetWeakPtr(), fired_watch, child, created, |
+ deleted, is_dir)); |
+} |
+void FilePathWatcherImpl::OnFilePathChangedOnOriginSequence( |
+ InotifyReader::Watch fired_watch, |
+ const FilePath::StringType& child, |
+ bool created, |
+ bool deleted, |
+ bool is_dir) { |
DCHECK(task_runner()->RunsTasksOnCurrentThread()); |
+ DCHECK(!watches_.empty()); |
DCHECK(HasValidWatchVector()); |
// Used below to avoid multiple recursive updates. |
@@ -437,41 +445,23 @@ bool FilePathWatcherImpl::Watch(const FilePath& path, |
} |
void FilePathWatcherImpl::Cancel() { |
- if (callback_.is_null()) { |
- // Watch was never called, or the message_loop() thread is already gone. |
+ if (!callback_) { |
+ // Watch() was never called. |
set_cancelled(); |
return; |
} |
- // Switch to the task_runner() if necessary so we can access |watches_|. |
- if (!task_runner()->RunsTasksOnCurrentThread()) { |
- task_runner()->PostTask( |
- FROM_HERE, |
- Bind(&FilePathWatcherImpl::CancelOnMessageLoopThreadOrInDestructor, |
- this)); |
- } else { |
- CancelOnMessageLoopThreadOrInDestructor(); |
- } |
-} |
- |
-void FilePathWatcherImpl::CancelOnMessageLoopThreadOrInDestructor() { |
- DCHECK(in_destructor_ || task_runner()->RunsTasksOnCurrentThread()); |
- |
- if (is_cancelled()) |
- return; |
+ DCHECK(task_runner()->RunsTasksOnCurrentThread()); |
+ DCHECK(!is_cancelled()); |
set_cancelled(); |
- |
- if (!callback_.is_null()) |
- callback_.Reset(); |
+ callback_.Reset(); |
for (size_t i = 0; i < watches_.size(); ++i) |
g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); |
watches_.clear(); |
target_.clear(); |
- |
- if (recursive_) |
- RemoveRecursiveWatches(); |
+ RemoveRecursiveWatches(); |
} |
void FilePathWatcherImpl::UpdateWatches() { |
@@ -657,7 +647,7 @@ bool FilePathWatcherImpl::HasValidWatchVector() const { |
FilePathWatcher::FilePathWatcher() { |
sequence_checker_.DetachFromSequence(); |
- impl_ = new FilePathWatcherImpl(); |
+ impl_ = MakeUnique<FilePathWatcherImpl>(); |
} |
} // namespace base |