| Index: base/files/file_path_watcher_fsevents.cc
|
| diff --git a/base/files/file_path_watcher_fsevents.cc b/base/files/file_path_watcher_fsevents.cc
|
| index 8e73b557aa291d7967ffb84d19183316cc619c1c..e9a87b0e0526a4e2b3e78f5dbff85ceba3513d2a 100644
|
| --- a/base/files/file_path_watcher_fsevents.cc
|
| +++ b/base/files/file_path_watcher_fsevents.cc
|
| @@ -13,7 +13,6 @@
|
| #include "base/lazy_instance.h"
|
| #include "base/logging.h"
|
| #include "base/mac/scoped_cftyperef.h"
|
| -#include "base/macros.h"
|
| #include "base/strings/stringprintf.h"
|
| #include "base/threading/sequenced_task_runner_handle.h"
|
|
|
| @@ -69,10 +68,16 @@ FilePath ResolvePath(const FilePath& path) {
|
|
|
| FilePathWatcherFSEvents::FilePathWatcherFSEvents()
|
| : queue_(dispatch_queue_create(
|
| - base::StringPrintf(
|
| - "org.chromium.base.FilePathWatcher.%p", this).c_str(),
|
| + base::StringPrintf("org.chromium.base.FilePathWatcher.%p", this)
|
| + .c_str(),
|
| DISPATCH_QUEUE_SERIAL)),
|
| - fsevent_stream_(nullptr) {
|
| + fsevent_stream_(nullptr),
|
| + weak_factory_(this) {}
|
| +
|
| +FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {
|
| + DCHECK(!task_runner() || task_runner()->RunsTasksOnCurrentThread());
|
| + DCHECK(callback_.is_null())
|
| + << "Cancel() must be called before FilePathWatcher is destroyed.";
|
| }
|
|
|
| bool FilePathWatcherFSEvents::Watch(const FilePath& path,
|
| @@ -105,11 +110,15 @@ void FilePathWatcherFSEvents::Cancel() {
|
| set_cancelled();
|
| callback_.Reset();
|
|
|
| - // Switch to the dispatch queue to tear down the event stream. As the queue
|
| - // is owned by this object, and this method is called from the destructor,
|
| - // execute the block synchronously.
|
| + // Switch to the dispatch queue to tear down the event stream. As the queue is
|
| + // owned by |this|, and this method is called from the destructor, execute the
|
| + // block synchronously.
|
| dispatch_sync(queue_, ^{
|
| - CancelOnMessageLoopThread();
|
| + if (fsevent_stream_) {
|
| + DestroyEventStream();
|
| + target_.clear();
|
| + resolved_target_.clear();
|
| + }
|
| });
|
| }
|
|
|
| @@ -140,31 +149,40 @@ void FilePathWatcherFSEvents::FSEventsCallback(
|
| // the directory to be watched gets created.
|
| if (root_changed) {
|
| // Resetting the event stream from within the callback fails (FSEvents spews
|
| - // bad file descriptor errors), so post a task to do the reset.
|
| - dispatch_async(watcher->queue_, ^{
|
| - watcher->UpdateEventStream(root_change_at);
|
| - });
|
| + // bad file descriptor errors), so do the reset asynchronously.
|
| + //
|
| + // We can't dispatch_async a call to UpdateEventStream() directly because
|
| + // there would be no guarantee that |watcher| still exists when it runs.
|
| + //
|
| + // Instead, bounce on task_runner() and use a WeakPtr to verify that
|
| + // |watcher| still exists. If it does, dispatch_async a call to
|
| + // UpdateEventStream(). Because the destructor of |watcher| runs on
|
| + // task_runner() and calls dispatch_sync, it is guaranteed that |watcher|
|
| + // still exists when UpdateEventStream() runs.
|
| + watcher->task_runner()->PostTask(
|
| + FROM_HERE, Bind(
|
| + [](WeakPtr<FilePathWatcherFSEvents> weak_watcher,
|
| + FSEventStreamEventId root_change_at) {
|
| + if (!weak_watcher)
|
| + return;
|
| + FilePathWatcherFSEvents* watcher = weak_watcher.get();
|
| + dispatch_async(watcher->queue_, ^{
|
| + watcher->UpdateEventStream(root_change_at);
|
| + });
|
| + },
|
| + watcher->weak_factory_.GetWeakPtr(), root_change_at));
|
| }
|
|
|
| watcher->OnFilePathsChanged(paths);
|
| }
|
|
|
| -FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {
|
| - // This method may be called on either the libdispatch or task_runner()
|
| - // thread. Checking callback_ on the libdispatch thread here is safe because
|
| - // it is executing in a task posted by Cancel() which first reset callback_.
|
| - // PostTask forms a sufficient memory barrier to ensure that the value is
|
| - // consistent on the target thread.
|
| - DCHECK(callback_.is_null())
|
| - << "Cancel() must be called before FilePathWatcher is destroyed.";
|
| -}
|
| -
|
| void FilePathWatcherFSEvents::OnFilePathsChanged(
|
| const std::vector<FilePath>& paths) {
|
| DCHECK(!resolved_target_.empty());
|
| task_runner()->PostTask(
|
| - FROM_HERE, Bind(&FilePathWatcherFSEvents::DispatchEvents, this, paths,
|
| - target_, resolved_target_));
|
| + FROM_HERE,
|
| + Bind(&FilePathWatcherFSEvents::DispatchEvents, weak_factory_.GetWeakPtr(),
|
| + paths, target_, resolved_target_));
|
| }
|
|
|
| void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths,
|
| @@ -185,18 +203,6 @@ void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths,
|
| }
|
| }
|
|
|
| -void FilePathWatcherFSEvents::CancelOnMessageLoopThread() {
|
| - // For all other implementations, the "message loop thread" is the IO thread,
|
| - // as returned by task_runner(). This implementation, however, needs to
|
| - // cancel pending work on the Dispatch Queue thread.
|
| -
|
| - if (fsevent_stream_) {
|
| - DestroyEventStream();
|
| - target_.clear();
|
| - resolved_target_.clear();
|
| - }
|
| -}
|
| -
|
| void FilePathWatcherFSEvents::UpdateEventStream(
|
| FSEventStreamEventId start_event) {
|
| // It can happen that the watcher gets canceled while tasks that call this
|
| @@ -232,8 +238,9 @@ void FilePathWatcherFSEvents::UpdateEventStream(
|
| FSEventStreamSetDispatchQueue(fsevent_stream_, queue_);
|
|
|
| if (!FSEventStreamStart(fsevent_stream_)) {
|
| - task_runner()->PostTask(
|
| - FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_));
|
| + task_runner()->PostTask(FROM_HERE,
|
| + Bind(&FilePathWatcherFSEvents::ReportError,
|
| + weak_factory_.GetWeakPtr(), target_));
|
| }
|
| }
|
|
|
| @@ -242,8 +249,9 @@ bool FilePathWatcherFSEvents::ResolveTargetPath() {
|
| bool changed = resolved != resolved_target_;
|
| resolved_target_ = resolved;
|
| if (resolved_target_.empty()) {
|
| - task_runner()->PostTask(
|
| - FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_));
|
| + task_runner()->PostTask(FROM_HERE,
|
| + Bind(&FilePathWatcherFSEvents::ReportError,
|
| + weak_factory_.GetWeakPtr(), target_));
|
| }
|
| return changed;
|
| }
|
|
|