| 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/file_util.h" | 23 #include "base/file_util.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/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().get()); | 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 |