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 <string.h> | 8 #include <string.h> |
9 #include <sys/inotify.h> | 9 #include <sys/inotify.h> |
10 #include <sys/ioctl.h> | 10 #include <sys/ioctl.h> |
(...skipping 10 matching lines...) Expand all Loading... |
21 #include "base/containers/hash_tables.h" | 21 #include "base/containers/hash_tables.h" |
22 #include "base/debug/trace_event.h" | 22 #include "base/debug/trace_event.h" |
23 #include "base/files/file_enumerator.h" | 23 #include "base/files/file_enumerator.h" |
24 #include "base/files/file_path.h" | 24 #include "base/files/file_path.h" |
25 #include "base/files/file_util.h" | 25 #include "base/files/file_util.h" |
26 #include "base/lazy_instance.h" | 26 #include "base/lazy_instance.h" |
27 #include "base/location.h" | 27 #include "base/location.h" |
28 #include "base/logging.h" | 28 #include "base/logging.h" |
29 #include "base/memory/scoped_ptr.h" | 29 #include "base/memory/scoped_ptr.h" |
30 #include "base/message_loop/message_loop.h" | 30 #include "base/message_loop/message_loop.h" |
31 #include "base/message_loop/message_loop_proxy.h" | |
32 #include "base/posix/eintr_wrapper.h" | 31 #include "base/posix/eintr_wrapper.h" |
33 #include "base/synchronization/lock.h" | 32 #include "base/synchronization/lock.h" |
| 33 #include "base/thread_task_runner_handle.h" |
34 #include "base/threading/thread.h" | 34 #include "base/threading/thread.h" |
35 | 35 |
36 namespace base { | 36 namespace base { |
37 | 37 |
38 namespace { | 38 namespace { |
39 | 39 |
40 class FilePathWatcherImpl; | 40 class FilePathWatcherImpl; |
41 | 41 |
42 // Singleton to manage all inotify watches. | 42 // Singleton to manage all inotify watches. |
43 // TODO(tony): It would be nice if this wasn't a singleton. | 43 // TODO(tony): It would be nice if this wasn't a singleton. |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
110 private: | 110 private: |
111 // Start watching |path| for changes and notify |delegate| on each change. | 111 // Start watching |path| for changes and notify |delegate| on each change. |
112 // Returns true if watch for |path| has been added successfully. | 112 // Returns true if watch for |path| has been added successfully. |
113 virtual bool Watch(const FilePath& path, | 113 virtual bool Watch(const FilePath& path, |
114 bool recursive, | 114 bool recursive, |
115 const FilePathWatcher::Callback& callback) override; | 115 const FilePathWatcher::Callback& callback) override; |
116 | 116 |
117 // Cancel the watch. This unregisters the instance with InotifyReader. | 117 // Cancel the watch. This unregisters the instance with InotifyReader. |
118 virtual void Cancel() override; | 118 virtual void Cancel() override; |
119 | 119 |
120 // Cleans up and stops observing the message_loop() thread. | 120 // Cleans up and stops observing the task_runner() thread. |
121 virtual void CancelOnMessageLoopThread() override; | 121 virtual void CancelOnMessageLoopThread() override; |
122 | 122 |
123 // Deletion of the FilePathWatcher will call Cancel() to dispose of this | 123 // Deletion of the FilePathWatcher will call Cancel() to dispose of this |
124 // object in the right thread. This also observes destruction of the required | 124 // object in the right thread. This also observes destruction of the required |
125 // cleanup thread, in case it quits before Cancel() is called. | 125 // cleanup thread, in case it quits before Cancel() is called. |
126 virtual void WillDestroyCurrentMessageLoop() override; | 126 virtual void WillDestroyCurrentMessageLoop() override; |
127 | 127 |
128 // Inotify watches are installed for all directory components of |target_|. A | 128 // Inotify watches are installed for all directory components of |target_|. A |
129 // WatchEntry instance holds the watch descriptor for a component and the | 129 // WatchEntry instance holds the watch descriptor for a component and the |
130 // subdirectory for that identifies the next component. If a symbolic link | 130 // subdirectory for that identifies the next component. If a symbolic link |
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
339 | 339 |
340 FilePathWatcherImpl::FilePathWatcherImpl() | 340 FilePathWatcherImpl::FilePathWatcherImpl() |
341 : recursive_(false) { | 341 : recursive_(false) { |
342 } | 342 } |
343 | 343 |
344 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, | 344 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, |
345 const FilePath::StringType& child, | 345 const FilePath::StringType& child, |
346 bool created, | 346 bool created, |
347 bool deleted, | 347 bool deleted, |
348 bool is_dir) { | 348 bool is_dir) { |
349 if (!message_loop()->BelongsToCurrentThread()) { | 349 if (!task_runner()->RunsTasksOnCurrentThread()) { |
350 // Switch to message_loop() to access |watches_| safely. | 350 // Switch to task_runner() to access |watches_| safely. |
351 message_loop()->PostTask( | 351 task_runner()->PostTask(FROM_HERE, |
352 FROM_HERE, | 352 Bind(&FilePathWatcherImpl::OnFilePathChanged, |
353 Bind(&FilePathWatcherImpl::OnFilePathChanged, this, | 353 this, |
354 fired_watch, child, created, deleted, is_dir)); | 354 fired_watch, |
| 355 child, |
| 356 created, |
| 357 deleted, |
| 358 is_dir)); |
355 return; | 359 return; |
356 } | 360 } |
357 | 361 |
358 // Check to see if CancelOnMessageLoopThread() has already been called. | 362 // Check to see if CancelOnMessageLoopThread() has already been called. |
359 // May happen when code flow reaches here from the PostTask() above. | 363 // May happen when code flow reaches here from the PostTask() above. |
360 if (watches_.empty()) { | 364 if (watches_.empty()) { |
361 DCHECK(target_.empty()); | 365 DCHECK(target_.empty()); |
362 return; | 366 return; |
363 } | 367 } |
364 | 368 |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
422 callback_.Run(target_, false /* error */); | 426 callback_.Run(target_, false /* error */); |
423 } | 427 } |
424 } | 428 } |
425 | 429 |
426 bool FilePathWatcherImpl::Watch(const FilePath& path, | 430 bool FilePathWatcherImpl::Watch(const FilePath& path, |
427 bool recursive, | 431 bool recursive, |
428 const FilePathWatcher::Callback& callback) { | 432 const FilePathWatcher::Callback& callback) { |
429 DCHECK(target_.empty()); | 433 DCHECK(target_.empty()); |
430 DCHECK(MessageLoopForIO::current()); | 434 DCHECK(MessageLoopForIO::current()); |
431 | 435 |
432 set_message_loop(MessageLoopProxy::current()); | 436 set_task_runner(ThreadTaskRunnerHandle::Get()); |
433 callback_ = callback; | 437 callback_ = callback; |
434 target_ = path; | 438 target_ = path; |
435 recursive_ = recursive; | 439 recursive_ = recursive; |
436 MessageLoop::current()->AddDestructionObserver(this); | 440 MessageLoop::current()->AddDestructionObserver(this); |
437 | 441 |
438 std::vector<FilePath::StringType> comps; | 442 std::vector<FilePath::StringType> comps; |
439 target_.GetComponents(&comps); | 443 target_.GetComponents(&comps); |
440 DCHECK(!comps.empty()); | 444 DCHECK(!comps.empty()); |
441 for (size_t i = 1; i < comps.size(); ++i) | 445 for (size_t i = 1; i < comps.size(); ++i) |
442 watches_.push_back(WatchEntry(comps[i])); | 446 watches_.push_back(WatchEntry(comps[i])); |
443 watches_.push_back(WatchEntry(FilePath::StringType())); | 447 watches_.push_back(WatchEntry(FilePath::StringType())); |
444 UpdateWatches(); | 448 UpdateWatches(); |
445 return true; | 449 return true; |
446 } | 450 } |
447 | 451 |
448 void FilePathWatcherImpl::Cancel() { | 452 void FilePathWatcherImpl::Cancel() { |
449 if (callback_.is_null()) { | 453 if (callback_.is_null()) { |
450 // Watch was never called, or the message_loop() thread is already gone. | 454 // Watch was never called, or the task_runner() thread is already gone. |
451 set_cancelled(); | 455 set_cancelled(); |
452 return; | 456 return; |
453 } | 457 } |
454 | 458 |
455 // Switch to the message_loop() if necessary so we can access |watches_|. | 459 // Switch to the task_runner() if necessary so we can access |watches_|. |
456 if (!message_loop()->BelongsToCurrentThread()) { | 460 if (!task_runner()->RunsTasksOnCurrentThread()) { |
457 message_loop()->PostTask(FROM_HERE, | 461 task_runner()->PostTask( |
458 Bind(&FilePathWatcher::CancelWatch, | 462 FROM_HERE, |
459 make_scoped_refptr(this))); | 463 Bind(&FilePathWatcher::CancelWatch, make_scoped_refptr(this))); |
460 } else { | 464 } else { |
461 CancelOnMessageLoopThread(); | 465 CancelOnMessageLoopThread(); |
462 } | 466 } |
463 } | 467 } |
464 | 468 |
465 void FilePathWatcherImpl::CancelOnMessageLoopThread() { | 469 void FilePathWatcherImpl::CancelOnMessageLoopThread() { |
466 DCHECK(message_loop()->BelongsToCurrentThread()); | 470 DCHECK(task_runner()->RunsTasksOnCurrentThread()); |
467 set_cancelled(); | 471 set_cancelled(); |
468 | 472 |
469 if (!callback_.is_null()) { | 473 if (!callback_.is_null()) { |
470 MessageLoop::current()->RemoveDestructionObserver(this); | 474 MessageLoop::current()->RemoveDestructionObserver(this); |
471 callback_.Reset(); | 475 callback_.Reset(); |
472 } | 476 } |
473 | 477 |
474 for (size_t i = 0; i < watches_.size(); ++i) | 478 for (size_t i = 0; i < watches_.size(); ++i) |
475 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); | 479 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); |
476 watches_.clear(); | 480 watches_.clear(); |
477 target_.clear(); | 481 target_.clear(); |
478 | 482 |
479 if (recursive_) | 483 if (recursive_) |
480 RemoveRecursiveWatches(); | 484 RemoveRecursiveWatches(); |
481 } | 485 } |
482 | 486 |
483 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { | 487 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { |
484 CancelOnMessageLoopThread(); | 488 CancelOnMessageLoopThread(); |
485 } | 489 } |
486 | 490 |
487 void FilePathWatcherImpl::UpdateWatches() { | 491 void FilePathWatcherImpl::UpdateWatches() { |
488 // Ensure this runs on the message_loop() exclusively in order to avoid | 492 // Ensure this runs on the task_runner() exclusively in order to avoid |
489 // concurrency issues. | 493 // concurrency issues. |
490 DCHECK(message_loop()->BelongsToCurrentThread()); | 494 DCHECK(task_runner()->RunsTasksOnCurrentThread()); |
491 DCHECK(HasValidWatchVector()); | 495 DCHECK(HasValidWatchVector()); |
492 | 496 |
493 // Walk the list of watches and update them as we go. | 497 // Walk the list of watches and update them as we go. |
494 FilePath path(FILE_PATH_LITERAL("/")); | 498 FilePath path(FILE_PATH_LITERAL("/")); |
495 bool path_valid = true; | 499 bool path_valid = true; |
496 for (size_t i = 0; i < watches_.size(); ++i) { | 500 for (size_t i = 0; i < watches_.size(); ++i) { |
497 WatchEntry& watch_entry = watches_[i]; | 501 WatchEntry& watch_entry = watches_[i]; |
498 InotifyReader::Watch old_watch = watch_entry.watch; | 502 InotifyReader::Watch old_watch = watch_entry.watch; |
499 watch_entry.watch = InotifyReader::kInvalidWatch; | 503 watch_entry.watch = InotifyReader::kInvalidWatch; |
500 watch_entry.linkname.clear(); | 504 watch_entry.linkname.clear(); |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
660 return watches_[watches_.size() - 1].subdir.empty(); | 664 return watches_[watches_.size() - 1].subdir.empty(); |
661 } | 665 } |
662 | 666 |
663 } // namespace | 667 } // namespace |
664 | 668 |
665 FilePathWatcher::FilePathWatcher() { | 669 FilePathWatcher::FilePathWatcher() { |
666 impl_ = new FilePathWatcherImpl(); | 670 impl_ = new FilePathWatcherImpl(); |
667 } | 671 } |
668 | 672 |
669 } // namespace base | 673 } // namespace base |
OLD | NEW |