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