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/ptr_util.h" |
| 32 #include "base/memory/weak_ptr.h" |
31 #include "base/posix/eintr_wrapper.h" | 33 #include "base/posix/eintr_wrapper.h" |
32 #include "base/single_thread_task_runner.h" | 34 #include "base/single_thread_task_runner.h" |
33 #include "base/stl_util.h" | 35 #include "base/stl_util.h" |
34 #include "base/synchronization/lock.h" | 36 #include "base/synchronization/lock.h" |
35 #include "base/threading/sequenced_task_runner_handle.h" | 37 #include "base/threading/sequenced_task_runner_handle.h" |
36 #include "base/threading/thread.h" | 38 #include "base/threading/thread.h" |
37 #include "base/trace_event/trace_event.h" | 39 #include "base/trace_event/trace_event.h" |
38 | 40 |
39 namespace base { | 41 namespace base { |
40 | 42 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 | 86 |
85 // Flag set to true when startup was successful. | 87 // Flag set to true when startup was successful. |
86 bool valid_; | 88 bool valid_; |
87 | 89 |
88 DISALLOW_COPY_AND_ASSIGN(InotifyReader); | 90 DISALLOW_COPY_AND_ASSIGN(InotifyReader); |
89 }; | 91 }; |
90 | 92 |
91 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { | 93 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { |
92 public: | 94 public: |
93 FilePathWatcherImpl(); | 95 FilePathWatcherImpl(); |
| 96 ~FilePathWatcherImpl() override; |
94 | 97 |
95 // Called for each event coming from the watch. |fired_watch| identifies the | 98 // Called for each event coming from the watch. |fired_watch| identifies the |
96 // watch that fired, |child| indicates what has changed, and is relative to | 99 // watch that fired, |child| indicates what has changed, and is relative to |
97 // the currently watched path for |fired_watch|. | 100 // the currently watched path for |fired_watch|. |
98 // | 101 // |
99 // |created| is true if the object appears. | 102 // |created| is true if the object appears. |
100 // |deleted| is true if the object disappears. | 103 // |deleted| is true if the object disappears. |
101 // |is_dir| is true if the object is a directory. | 104 // |is_dir| is true if the object is a directory. |
102 void OnFilePathChanged(InotifyReader::Watch fired_watch, | 105 void OnFilePathChanged(InotifyReader::Watch fired_watch, |
103 const FilePath::StringType& child, | 106 const FilePath::StringType& child, |
104 bool created, | 107 bool created, |
105 bool deleted, | 108 bool deleted, |
106 bool is_dir); | 109 bool is_dir); |
107 | 110 |
108 protected: | 111 private: |
109 ~FilePathWatcherImpl() override { | 112 void OnFilePathChangedOnOriginSequence(InotifyReader::Watch fired_watch, |
110 in_destructor_ = true; | 113 const FilePath::StringType& child, |
111 CancelOnMessageLoopThreadOrInDestructor(); | 114 bool created, |
112 } | 115 bool deleted, |
| 116 bool is_dir); |
113 | 117 |
114 private: | |
115 // Start watching |path| for changes and notify |delegate| on each change. | 118 // Start watching |path| for changes and notify |delegate| on each change. |
116 // Returns true if watch for |path| has been added successfully. | 119 // Returns true if watch for |path| has been added successfully. |
117 bool Watch(const FilePath& path, | 120 bool Watch(const FilePath& path, |
118 bool recursive, | 121 bool recursive, |
119 const FilePathWatcher::Callback& callback) override; | 122 const FilePathWatcher::Callback& callback) override; |
120 | 123 |
121 // Cancel the watch. This unregisters the instance with InotifyReader. | 124 // Cancel the watch. This unregisters the instance with InotifyReader. |
122 void Cancel() override; | 125 void Cancel() override; |
123 void CancelOnMessageLoopThreadOrInDestructor(); | |
124 | 126 |
125 // Inotify watches are installed for all directory components of |target_|. | 127 // Inotify watches are installed for all directory components of |target_|. |
126 // A WatchEntry instance holds: | 128 // A WatchEntry instance holds: |
127 // - |watch|: the watch descriptor for a component. | 129 // - |watch|: the watch descriptor for a component. |
128 // - |subdir|: the subdirectory that identifies the next component. | 130 // - |subdir|: the subdirectory that identifies the next component. |
129 // - For the last component, there is no next component, so it is empty. | 131 // - For the last component, there is no next component, so it is empty. |
130 // - |linkname|: the target of the symlink. | 132 // - |linkname|: the target of the symlink. |
131 // - Only if the target being watched is a symbolic link. | 133 // - Only if the target being watched is a symbolic link. |
132 struct WatchEntry { | 134 struct WatchEntry { |
133 explicit WatchEntry(const FilePath::StringType& dirname) | 135 explicit WatchEntry(const FilePath::StringType& dirname) |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
178 bool recursive_; | 180 bool recursive_; |
179 | 181 |
180 // The vector of watches and next component names for all path components, | 182 // 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 | 183 // 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|. | 184 // |target_| and always stores an empty next component name in |subdir|. |
183 WatchVector watches_; | 185 WatchVector watches_; |
184 | 186 |
185 hash_map<InotifyReader::Watch, FilePath> recursive_paths_by_watch_; | 187 hash_map<InotifyReader::Watch, FilePath> recursive_paths_by_watch_; |
186 std::map<FilePath, InotifyReader::Watch> recursive_watches_by_path_; | 188 std::map<FilePath, InotifyReader::Watch> recursive_watches_by_path_; |
187 | 189 |
188 bool in_destructor_ = false; | 190 WeakPtrFactory<FilePathWatcherImpl> weak_factory_; |
189 | 191 |
190 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); | 192 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); |
191 }; | 193 }; |
192 | 194 |
193 void InotifyReaderCallback(InotifyReader* reader, int inotify_fd) { | 195 void InotifyReaderCallback(InotifyReader* reader, int inotify_fd) { |
194 // Make sure the file descriptors are good for use with select(). | 196 // Make sure the file descriptors are good for use with select(). |
195 CHECK_LE(0, inotify_fd); | 197 CHECK_LE(0, inotify_fd); |
196 CHECK_GT(FD_SETSIZE, inotify_fd); | 198 CHECK_GT(FD_SETSIZE, inotify_fd); |
197 | 199 |
198 trace_event::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop(); | 200 trace_event::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop(); |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
305 ++watcher) { | 307 ++watcher) { |
306 (*watcher)->OnFilePathChanged(event->wd, | 308 (*watcher)->OnFilePathChanged(event->wd, |
307 child, | 309 child, |
308 event->mask & (IN_CREATE | IN_MOVED_TO), | 310 event->mask & (IN_CREATE | IN_MOVED_TO), |
309 event->mask & (IN_DELETE | IN_MOVED_FROM), | 311 event->mask & (IN_DELETE | IN_MOVED_FROM), |
310 event->mask & IN_ISDIR); | 312 event->mask & IN_ISDIR); |
311 } | 313 } |
312 } | 314 } |
313 | 315 |
314 FilePathWatcherImpl::FilePathWatcherImpl() | 316 FilePathWatcherImpl::FilePathWatcherImpl() |
315 : recursive_(false) { | 317 : recursive_(false), weak_factory_(this) {} |
| 318 |
| 319 FilePathWatcherImpl::~FilePathWatcherImpl() { |
| 320 DCHECK(!task_runner() || task_runner()->RunsTasksOnCurrentThread()); |
316 } | 321 } |
317 | 322 |
318 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, | 323 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, |
319 const FilePath::StringType& child, | 324 const FilePath::StringType& child, |
320 bool created, | 325 bool created, |
321 bool deleted, | 326 bool deleted, |
322 bool is_dir) { | 327 bool is_dir) { |
323 if (!task_runner()->RunsTasksOnCurrentThread()) { | 328 DCHECK(!task_runner()->RunsTasksOnCurrentThread()); |
324 // Switch to task_runner() to access |watches_| safely. | |
325 task_runner()->PostTask(FROM_HERE, | |
326 Bind(&FilePathWatcherImpl::OnFilePathChanged, this, | |
327 fired_watch, child, created, deleted, is_dir)); | |
328 return; | |
329 } | |
330 | 329 |
331 // Check to see if CancelOnMessageLoopThreadOrInDestructor() has already been | 330 // This method is invoked on the Inotify thread. Switch to task_runner() to |
332 // called. May happen when code flow reaches here from the PostTask() above. | 331 // access |watches_| safely. Use a WeakPtr to prevent the callback from |
333 if (watches_.empty()) { | 332 // running after |this| is destroyed (i.e. after the watch is cancelled). |
334 DCHECK(target_.empty()); | 333 task_runner()->PostTask( |
335 return; | 334 FROM_HERE, Bind(&FilePathWatcherImpl::OnFilePathChangedOnOriginSequence, |
336 } | 335 weak_factory_.GetWeakPtr(), fired_watch, child, created, |
| 336 deleted, is_dir)); |
| 337 } |
337 | 338 |
| 339 void FilePathWatcherImpl::OnFilePathChangedOnOriginSequence( |
| 340 InotifyReader::Watch fired_watch, |
| 341 const FilePath::StringType& child, |
| 342 bool created, |
| 343 bool deleted, |
| 344 bool is_dir) { |
338 DCHECK(task_runner()->RunsTasksOnCurrentThread()); | 345 DCHECK(task_runner()->RunsTasksOnCurrentThread()); |
| 346 DCHECK(!watches_.empty()); |
339 DCHECK(HasValidWatchVector()); | 347 DCHECK(HasValidWatchVector()); |
340 | 348 |
341 // Used below to avoid multiple recursive updates. | 349 // Used below to avoid multiple recursive updates. |
342 bool did_update = false; | 350 bool did_update = false; |
343 | 351 |
344 // Find the entry in |watches_| that corresponds to |fired_watch|. | 352 // Find the entry in |watches_| that corresponds to |fired_watch|. |
345 for (size_t i = 0; i < watches_.size(); ++i) { | 353 for (size_t i = 0; i < watches_.size(); ++i) { |
346 const WatchEntry& watch_entry = watches_[i]; | 354 const WatchEntry& watch_entry = watches_[i]; |
347 if (fired_watch != watch_entry.watch) | 355 if (fired_watch != watch_entry.watch) |
348 continue; | 356 continue; |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
430 target_.GetComponents(&comps); | 438 target_.GetComponents(&comps); |
431 DCHECK(!comps.empty()); | 439 DCHECK(!comps.empty()); |
432 for (size_t i = 1; i < comps.size(); ++i) | 440 for (size_t i = 1; i < comps.size(); ++i) |
433 watches_.push_back(WatchEntry(comps[i])); | 441 watches_.push_back(WatchEntry(comps[i])); |
434 watches_.push_back(WatchEntry(FilePath::StringType())); | 442 watches_.push_back(WatchEntry(FilePath::StringType())); |
435 UpdateWatches(); | 443 UpdateWatches(); |
436 return true; | 444 return true; |
437 } | 445 } |
438 | 446 |
439 void FilePathWatcherImpl::Cancel() { | 447 void FilePathWatcherImpl::Cancel() { |
440 if (callback_.is_null()) { | 448 if (!callback_) { |
441 // Watch was never called, or the message_loop() thread is already gone. | 449 // Watch() was never called. |
442 set_cancelled(); | 450 set_cancelled(); |
443 return; | 451 return; |
444 } | 452 } |
445 | 453 |
446 // Switch to the task_runner() if necessary so we can access |watches_|. | 454 DCHECK(task_runner()->RunsTasksOnCurrentThread()); |
447 if (!task_runner()->RunsTasksOnCurrentThread()) { | 455 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()->RunsTasksOnCurrentThread()); | |
459 | |
460 if (is_cancelled()) | |
461 return; | |
462 | 456 |
463 set_cancelled(); | 457 set_cancelled(); |
464 | 458 callback_.Reset(); |
465 if (!callback_.is_null()) | |
466 callback_.Reset(); | |
467 | 459 |
468 for (size_t i = 0; i < watches_.size(); ++i) | 460 for (size_t i = 0; i < watches_.size(); ++i) |
469 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); | 461 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); |
470 watches_.clear(); | 462 watches_.clear(); |
471 target_.clear(); | 463 target_.clear(); |
472 | 464 RemoveRecursiveWatches(); |
473 if (recursive_) | |
474 RemoveRecursiveWatches(); | |
475 } | 465 } |
476 | 466 |
477 void FilePathWatcherImpl::UpdateWatches() { | 467 void FilePathWatcherImpl::UpdateWatches() { |
478 // Ensure this runs on the task_runner() exclusively in order to avoid | 468 // Ensure this runs on the task_runner() exclusively in order to avoid |
479 // concurrency issues. | 469 // concurrency issues. |
480 DCHECK(task_runner()->RunsTasksOnCurrentThread()); | 470 DCHECK(task_runner()->RunsTasksOnCurrentThread()); |
481 DCHECK(HasValidWatchVector()); | 471 DCHECK(HasValidWatchVector()); |
482 | 472 |
483 // Walk the list of watches and update them as we go. | 473 // Walk the list of watches and update them as we go. |
484 FilePath path(FILE_PATH_LITERAL("/")); | 474 FilePath path(FILE_PATH_LITERAL("/")); |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
650 if (watches_[i].subdir.empty()) | 640 if (watches_[i].subdir.empty()) |
651 return false; | 641 return false; |
652 } | 642 } |
653 return watches_.back().subdir.empty(); | 643 return watches_.back().subdir.empty(); |
654 } | 644 } |
655 | 645 |
656 } // namespace | 646 } // namespace |
657 | 647 |
658 FilePathWatcher::FilePathWatcher() { | 648 FilePathWatcher::FilePathWatcher() { |
659 sequence_checker_.DetachFromSequence(); | 649 sequence_checker_.DetachFromSequence(); |
660 impl_ = new FilePathWatcherImpl(); | 650 impl_ = MakeUnique<FilePathWatcherImpl>(); |
661 } | 651 } |
662 | 652 |
663 } // namespace base | 653 } // namespace base |
OLD | NEW |