Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1672)

Unified Diff: base/files/file_path_watcher_fsevents.cc

Issue 1046353004: Access fields from the appropriate threads in FilePathWatcherFSEvents. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add comments about thread usage. Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/files/file_path_watcher_fsevents.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 6e1a927a8def71d8e5841cdde84589ea0aff9c9e..ef4e6ee32eb412f3c51d70112a9d0d1824045aa0 100644
--- a/base/files/file_path_watcher_fsevents.cc
+++ b/base/files/file_path_watcher_fsevents.cc
@@ -75,11 +75,51 @@ FilePath ResolvePath(const FilePath& path) {
return result;
}
-// The callback passed to FSEventStreamCreate().
-void FSEventsCallback(ConstFSEventStreamRef stream,
- void* event_watcher, size_t num_events,
- void* event_paths, const FSEventStreamEventFlags flags[],
- const FSEventStreamEventId event_ids[]) {
+} // namespace
+
+FilePathWatcherFSEvents::FilePathWatcherFSEvents() : fsevent_stream_(NULL) {
+}
+
+bool FilePathWatcherFSEvents::Watch(const FilePath& path,
+ bool recursive,
+ const FilePathWatcher::Callback& callback) {
+ DCHECK(MessageLoopForIO::current());
+ DCHECK(!callback.is_null());
+ DCHECK(callback_.is_null());
+
+ // This class could support non-recursive watches, but that is currently
+ // left to FilePathWatcherKQueue.
+ if (!recursive)
+ return false;
+
+ set_message_loop(MessageLoopProxy::current());
+ callback_ = callback;
+
+ FSEventStreamEventId start_event = FSEventsGetCurrentEventId();
+ g_task_runner.Get().PostTask(
+ FROM_HERE, Bind(&FilePathWatcherFSEvents::StartEventStream, this,
+ start_event, path));
+ return true;
+}
+
+void FilePathWatcherFSEvents::Cancel() {
+ set_cancelled();
+ callback_.Reset();
+
+ // Switch to the dispatch queue thread to tear down the event stream.
+ g_task_runner.Get().PostTask(
+ FROM_HERE,
+ Bind(&FilePathWatcherFSEvents::CancelOnMessageLoopThread, this));
+}
+
+// static
+void FilePathWatcherFSEvents::FSEventsCallback(
+ ConstFSEventStreamRef stream,
+ void* event_watcher,
+ size_t num_events,
+ void* event_paths,
+ const FSEventStreamEventFlags flags[],
+ const FSEventStreamEventId event_ids[]) {
FilePathWatcherFSEvents* watcher =
reinterpret_cast<FilePathWatcherFSEvents*>(event_watcher);
DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
@@ -111,83 +151,51 @@ void FSEventsCallback(ConstFSEventStreamRef stream,
watcher->OnFilePathsChanged(paths);
}
-} // namespace
-
-FilePathWatcherFSEvents::FilePathWatcherFSEvents() : fsevent_stream_(NULL) {
+FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {
+ // This method may be called on either the libdispatch or message_loop()
+ // 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) {
- if (!message_loop()->BelongsToCurrentThread()) {
- message_loop()->PostTask(
- FROM_HERE,
- Bind(&FilePathWatcherFSEvents::OnFilePathsChanged, this, paths));
- return;
- }
-
- DCHECK(message_loop()->BelongsToCurrentThread());
- if (resolved_target_.empty())
- return;
-
- for (size_t i = 0; i < paths.size(); i++) {
- if (resolved_target_.IsParent(paths[i]) || resolved_target_ == paths[i]) {
- callback_.Run(target_, false);
- return;
- }
- }
+ DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
+ DCHECK(!resolved_target_.empty());
+ message_loop()->PostTask(
+ FROM_HERE, Bind(&FilePathWatcherFSEvents::DispatchEvents, this, paths,
+ target_, resolved_target_));
}
-bool FilePathWatcherFSEvents::Watch(const FilePath& path,
- bool recursive,
- const FilePathWatcher::Callback& callback) {
- DCHECK(resolved_target_.empty());
- DCHECK(MessageLoopForIO::current());
- DCHECK(!callback.is_null());
-
- // This class could support non-recursive watches, but that is currently
- // left to FilePathWatcherKQueue.
- if (!recursive)
- return false;
-
- set_message_loop(MessageLoopProxy::current());
- callback_ = callback;
- target_ = path;
-
- FSEventStreamEventId start_event = FSEventsGetCurrentEventId();
- g_task_runner.Get().PostTask(
- FROM_HERE,
- Bind(&FilePathWatcherFSEvents::StartEventStream, this, start_event));
- return true;
-}
+void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths,
+ const FilePath& target,
+ const FilePath& resolved_target) {
+ DCHECK(message_loop()->RunsTasksOnCurrentThread());
-void FilePathWatcherFSEvents::Cancel() {
- if (callback_.is_null()) {
- // Watch was never called, so exit.
- set_cancelled();
+ // Don't issue callbacks after Cancel() has been called.
+ if (is_cancelled() || callback_.is_null()) {
return;
}
- // Switch to the dispatch queue thread if necessary, so we can tear down
- // the event stream.
- if (!g_task_runner.Get().RunsTasksOnCurrentThread()) {
- g_task_runner.Get().PostTask(
- FROM_HERE,
- Bind(&FilePathWatcherFSEvents::CancelOnMessageLoopThread, this));
- } else {
- CancelOnMessageLoopThread();
+ for (const FilePath& path : paths) {
+ if (resolved_target.IsParent(path) || resolved_target == path) {
+ callback_.Run(target, false);
+ return;
+ }
}
}
void FilePathWatcherFSEvents::CancelOnMessageLoopThread() {
// For all other implementations, the "message loop thread" is the IO thread,
// as returned by message_loop(). This implementation, however, needs to
- // cancel pending work on the Dipatch Queue thread.
+ // cancel pending work on the Dispatch Queue thread.
DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
- set_cancelled();
if (fsevent_stream_) {
DestroyEventStream();
- callback_.Reset();
target_.clear();
resolved_target_.clear();
}
@@ -199,7 +207,7 @@ void FilePathWatcherFSEvents::UpdateEventStream(
// It can happen that the watcher gets canceled while tasks that call this
// function are still in flight, so abort if this situation is detected.
- if (is_cancelled() || resolved_target_.empty())
+ if (resolved_target_.empty())
return;
if (fsevent_stream_)
@@ -230,8 +238,10 @@ void FilePathWatcherFSEvents::UpdateEventStream(
FSEventStreamSetDispatchQueue(fsevent_stream_,
g_task_runner.Get().GetDispatchQueue());
- if (!FSEventStreamStart(fsevent_stream_))
- message_loop()->PostTask(FROM_HERE, Bind(callback_, target_, true));
+ if (!FSEventStreamStart(fsevent_stream_)) {
+ message_loop()->PostTask(
+ FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_));
+ }
}
bool FilePathWatcherFSEvents::ResolveTargetPath() {
@@ -239,11 +249,20 @@ bool FilePathWatcherFSEvents::ResolveTargetPath() {
FilePath resolved = ResolvePath(target_).StripTrailingSeparators();
bool changed = resolved != resolved_target_;
resolved_target_ = resolved;
- if (resolved_target_.empty())
- message_loop()->PostTask(FROM_HERE, Bind(callback_, target_, true));
+ if (resolved_target_.empty()) {
+ message_loop()->PostTask(
+ FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_));
+ }
return changed;
}
+void FilePathWatcherFSEvents::ReportError(const FilePath& target) {
+ DCHECK(message_loop()->RunsTasksOnCurrentThread());
+ if (!callback_.is_null()) {
+ callback_.Run(target, true);
+ }
+}
+
void FilePathWatcherFSEvents::DestroyEventStream() {
FSEventStreamStop(fsevent_stream_);
FSEventStreamInvalidate(fsevent_stream_);
@@ -251,16 +270,14 @@ void FilePathWatcherFSEvents::DestroyEventStream() {
fsevent_stream_ = NULL;
}
-void FilePathWatcherFSEvents::StartEventStream(
- FSEventStreamEventId start_event) {
+void FilePathWatcherFSEvents::StartEventStream(FSEventStreamEventId start_event,
+ const FilePath& path) {
DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
+ DCHECK(resolved_target_.empty());
+
+ target_ = path;
ResolveTargetPath();
UpdateEventStream(start_event);
}
-FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {
- DCHECK(!fsevent_stream_)
- << "File path watcher destroyed before event stream.";
-}
-
} // namespace base
« no previous file with comments | « base/files/file_path_watcher_fsevents.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698