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

Side by Side Diff: base/files/file_path_watcher_linux.cc

Issue 2596273003: Remove ref-counting from FilePathWatcher. (Closed)
Patch Set: self-review Created 3 years, 12 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698