| OLD | NEW | 
|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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_fsevents.h" | 5 #include "base/files/file_path_watcher_fsevents.h" | 
| 6 | 6 | 
| 7 #include <list> | 7 #include <list> | 
| 8 | 8 | 
| 9 #include "base/bind.h" | 9 #include "base/bind.h" | 
| 10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" | 
| 11 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" | 
| 12 #include "base/logging.h" | 12 #include "base/logging.h" | 
| 13 #include "base/mac/libdispatch_task_runner.h" | 13 #include "base/mac/libdispatch_task_runner.h" | 
| 14 #include "base/mac/scoped_cftyperef.h" | 14 #include "base/mac/scoped_cftyperef.h" | 
| 15 #include "base/message_loop/message_loop.h" | 15 #include "base/message_loop/message_loop.h" | 
|  | 16 #include "base/thread_task_runner_handle.h" | 
| 16 | 17 | 
| 17 namespace base { | 18 namespace base { | 
| 18 | 19 | 
| 19 namespace { | 20 namespace { | 
| 20 | 21 | 
| 21 // The latency parameter passed to FSEventsStreamCreate(). | 22 // The latency parameter passed to FSEventsStreamCreate(). | 
| 22 const CFAbsoluteTime kEventLatencySeconds = 0.3; | 23 const CFAbsoluteTime kEventLatencySeconds = 0.3; | 
| 23 | 24 | 
| 24 class FSEventsTaskRunner : public mac::LibDispatchTaskRunner { | 25 class FSEventsTaskRunner : public mac::LibDispatchTaskRunner { | 
| 25  public: | 26  public: | 
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 85                                     const FilePathWatcher::Callback& callback) { | 86                                     const FilePathWatcher::Callback& callback) { | 
| 86   DCHECK(MessageLoopForIO::current()); | 87   DCHECK(MessageLoopForIO::current()); | 
| 87   DCHECK(!callback.is_null()); | 88   DCHECK(!callback.is_null()); | 
| 88   DCHECK(callback_.is_null()); | 89   DCHECK(callback_.is_null()); | 
| 89 | 90 | 
| 90   // This class could support non-recursive watches, but that is currently | 91   // This class could support non-recursive watches, but that is currently | 
| 91   // left to FilePathWatcherKQueue. | 92   // left to FilePathWatcherKQueue. | 
| 92   if (!recursive) | 93   if (!recursive) | 
| 93     return false; | 94     return false; | 
| 94 | 95 | 
| 95   set_message_loop(MessageLoopProxy::current()); | 96   set_task_runner(ThreadTaskRunnerHandle::Get()); | 
| 96   callback_ = callback; | 97   callback_ = callback; | 
| 97 | 98 | 
| 98   FSEventStreamEventId start_event = FSEventsGetCurrentEventId(); | 99   FSEventStreamEventId start_event = FSEventsGetCurrentEventId(); | 
| 99   g_task_runner.Get().PostTask( | 100   g_task_runner.Get().PostTask( | 
| 100       FROM_HERE, Bind(&FilePathWatcherFSEvents::StartEventStream, this, | 101       FROM_HERE, Bind(&FilePathWatcherFSEvents::StartEventStream, this, | 
| 101                       start_event, path)); | 102                       start_event, path)); | 
| 102   return true; | 103   return true; | 
| 103 } | 104 } | 
| 104 | 105 | 
| 105 void FilePathWatcherFSEvents::Cancel() { | 106 void FilePathWatcherFSEvents::Cancel() { | 
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 145     g_task_runner.Get().PostTask( | 146     g_task_runner.Get().PostTask( | 
| 146         FROM_HERE, | 147         FROM_HERE, | 
| 147         Bind(&FilePathWatcherFSEvents::UpdateEventStream, watcher, | 148         Bind(&FilePathWatcherFSEvents::UpdateEventStream, watcher, | 
| 148              root_change_at)); | 149              root_change_at)); | 
| 149   } | 150   } | 
| 150 | 151 | 
| 151   watcher->OnFilePathsChanged(paths); | 152   watcher->OnFilePathsChanged(paths); | 
| 152 } | 153 } | 
| 153 | 154 | 
| 154 FilePathWatcherFSEvents::~FilePathWatcherFSEvents() { | 155 FilePathWatcherFSEvents::~FilePathWatcherFSEvents() { | 
| 155   // This method may be called on either the libdispatch or message_loop() | 156   // This method may be called on either the libdispatch or task_runner() | 
| 156   // thread. Checking callback_ on the libdispatch thread here is safe because | 157   // thread. Checking callback_ on the libdispatch thread here is safe because | 
| 157   // it is executing in a task posted by Cancel() which first reset callback_. | 158   // it is executing in a task posted by Cancel() which first reset callback_. | 
| 158   // PostTask forms a sufficient memory barrier to ensure that the value is | 159   // PostTask forms a sufficient memory barrier to ensure that the value is | 
| 159   // consistent on the target thread. | 160   // consistent on the target thread. | 
| 160   DCHECK(callback_.is_null()) | 161   DCHECK(callback_.is_null()) | 
| 161       << "Cancel() must be called before FilePathWatcher is destroyed."; | 162       << "Cancel() must be called before FilePathWatcher is destroyed."; | 
| 162 } | 163 } | 
| 163 | 164 | 
| 164 void FilePathWatcherFSEvents::OnFilePathsChanged( | 165 void FilePathWatcherFSEvents::OnFilePathsChanged( | 
| 165     const std::vector<FilePath>& paths) { | 166     const std::vector<FilePath>& paths) { | 
| 166   DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); | 167   DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); | 
| 167   DCHECK(!resolved_target_.empty()); | 168   DCHECK(!resolved_target_.empty()); | 
| 168   message_loop()->PostTask( | 169   task_runner()->PostTask( | 
| 169       FROM_HERE, Bind(&FilePathWatcherFSEvents::DispatchEvents, this, paths, | 170       FROM_HERE, Bind(&FilePathWatcherFSEvents::DispatchEvents, this, paths, | 
| 170                       target_, resolved_target_)); | 171                       target_, resolved_target_)); | 
| 171 } | 172 } | 
| 172 | 173 | 
| 173 void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths, | 174 void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths, | 
| 174                                              const FilePath& target, | 175                                              const FilePath& target, | 
| 175                                              const FilePath& resolved_target) { | 176                                              const FilePath& resolved_target) { | 
| 176   DCHECK(message_loop()->RunsTasksOnCurrentThread()); | 177   DCHECK(task_runner()->RunsTasksOnCurrentThread()); | 
| 177 | 178 | 
| 178   // Don't issue callbacks after Cancel() has been called. | 179   // Don't issue callbacks after Cancel() has been called. | 
| 179   if (is_cancelled() || callback_.is_null()) { | 180   if (is_cancelled() || callback_.is_null()) { | 
| 180     return; | 181     return; | 
| 181   } | 182   } | 
| 182 | 183 | 
| 183   for (const FilePath& path : paths) { | 184   for (const FilePath& path : paths) { | 
| 184     if (resolved_target.IsParent(path) || resolved_target == path) { | 185     if (resolved_target.IsParent(path) || resolved_target == path) { | 
| 185       callback_.Run(target, false); | 186       callback_.Run(target, false); | 
| 186       return; | 187       return; | 
| 187     } | 188     } | 
| 188   } | 189   } | 
| 189 } | 190 } | 
| 190 | 191 | 
| 191 void FilePathWatcherFSEvents::CancelOnMessageLoopThread() { | 192 void FilePathWatcherFSEvents::CancelOnMessageLoopThread() { | 
| 192   // For all other implementations, the "message loop thread" is the IO thread, | 193   // For all other implementations, the "message loop thread" is the IO thread, | 
| 193   // as returned by message_loop(). This implementation, however, needs to | 194   // as returned by task_runner(). This implementation, however, needs to | 
| 194   // cancel pending work on the Dispatch Queue thread. | 195   // cancel pending work on the Dispatch Queue thread. | 
| 195   DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); | 196   DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); | 
| 196 | 197 | 
| 197   if (fsevent_stream_) { | 198   if (fsevent_stream_) { | 
| 198     DestroyEventStream(); | 199     DestroyEventStream(); | 
| 199     target_.clear(); | 200     target_.clear(); | 
| 200     resolved_target_.clear(); | 201     resolved_target_.clear(); | 
| 201   } | 202   } | 
| 202 } | 203 } | 
| 203 | 204 | 
| (...skipping 28 matching lines...) Expand all  Loading... | 
| 232 | 233 | 
| 233   fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context, | 234   fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context, | 
| 234                                         watched_paths, | 235                                         watched_paths, | 
| 235                                         start_event, | 236                                         start_event, | 
| 236                                         kEventLatencySeconds, | 237                                         kEventLatencySeconds, | 
| 237                                         kFSEventStreamCreateFlagWatchRoot); | 238                                         kFSEventStreamCreateFlagWatchRoot); | 
| 238   FSEventStreamSetDispatchQueue(fsevent_stream_, | 239   FSEventStreamSetDispatchQueue(fsevent_stream_, | 
| 239                                 g_task_runner.Get().GetDispatchQueue()); | 240                                 g_task_runner.Get().GetDispatchQueue()); | 
| 240 | 241 | 
| 241   if (!FSEventStreamStart(fsevent_stream_)) { | 242   if (!FSEventStreamStart(fsevent_stream_)) { | 
| 242     message_loop()->PostTask( | 243     task_runner()->PostTask( | 
| 243         FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_)); | 244         FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_)); | 
| 244   } | 245   } | 
| 245 } | 246 } | 
| 246 | 247 | 
| 247 bool FilePathWatcherFSEvents::ResolveTargetPath() { | 248 bool FilePathWatcherFSEvents::ResolveTargetPath() { | 
| 248   DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); | 249   DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); | 
| 249   FilePath resolved = ResolvePath(target_).StripTrailingSeparators(); | 250   FilePath resolved = ResolvePath(target_).StripTrailingSeparators(); | 
| 250   bool changed = resolved != resolved_target_; | 251   bool changed = resolved != resolved_target_; | 
| 251   resolved_target_ = resolved; | 252   resolved_target_ = resolved; | 
| 252   if (resolved_target_.empty()) { | 253   if (resolved_target_.empty()) { | 
| 253     message_loop()->PostTask( | 254     task_runner()->PostTask( | 
| 254         FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_)); | 255         FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_)); | 
| 255   } | 256   } | 
| 256   return changed; | 257   return changed; | 
| 257 } | 258 } | 
| 258 | 259 | 
| 259 void FilePathWatcherFSEvents::ReportError(const FilePath& target) { | 260 void FilePathWatcherFSEvents::ReportError(const FilePath& target) { | 
| 260   DCHECK(message_loop()->RunsTasksOnCurrentThread()); | 261   DCHECK(task_runner()->RunsTasksOnCurrentThread()); | 
| 261   if (!callback_.is_null()) { | 262   if (!callback_.is_null()) { | 
| 262     callback_.Run(target, true); | 263     callback_.Run(target, true); | 
| 263   } | 264   } | 
| 264 } | 265 } | 
| 265 | 266 | 
| 266 void FilePathWatcherFSEvents::DestroyEventStream() { | 267 void FilePathWatcherFSEvents::DestroyEventStream() { | 
| 267   FSEventStreamStop(fsevent_stream_); | 268   FSEventStreamStop(fsevent_stream_); | 
| 268   FSEventStreamInvalidate(fsevent_stream_); | 269   FSEventStreamInvalidate(fsevent_stream_); | 
| 269   FSEventStreamRelease(fsevent_stream_); | 270   FSEventStreamRelease(fsevent_stream_); | 
| 270   fsevent_stream_ = NULL; | 271   fsevent_stream_ = NULL; | 
| 271 } | 272 } | 
| 272 | 273 | 
| 273 void FilePathWatcherFSEvents::StartEventStream(FSEventStreamEventId start_event, | 274 void FilePathWatcherFSEvents::StartEventStream(FSEventStreamEventId start_event, | 
| 274                                                const FilePath& path) { | 275                                                const FilePath& path) { | 
| 275   DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); | 276   DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); | 
| 276   DCHECK(resolved_target_.empty()); | 277   DCHECK(resolved_target_.empty()); | 
| 277 | 278 | 
| 278   target_ = path; | 279   target_ = path; | 
| 279   ResolveTargetPath(); | 280   ResolveTargetPath(); | 
| 280   UpdateEventStream(start_event); | 281   UpdateEventStream(start_event); | 
| 281 } | 282 } | 
| 282 | 283 | 
| 283 }  // namespace base | 284 }  // namespace base | 
| OLD | NEW | 
|---|