| 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> |
| 11 #include <sys/select.h> | 11 #include <sys/select.h> |
| 12 #include <unistd.h> | 12 #include <unistd.h> |
| 13 | 13 |
| 14 #include <algorithm> | 14 #include <algorithm> |
| 15 #include <map> | 15 #include <map> |
| 16 #include <set> | 16 #include <set> |
| 17 #include <utility> | 17 #include <utility> |
| 18 #include <vector> | 18 #include <vector> |
| 19 | 19 |
| 20 #include "base/bind.h" | 20 #include "base/bind.h" |
| 21 #include "base/containers/hash_tables.h" | 21 #include "base/containers/hash_tables.h" |
| 22 #include "base/files/file_enumerator.h" | 22 #include "base/files/file_enumerator.h" |
| 23 #include "base/files/file_path.h" | 23 #include "base/files/file_path.h" |
| 24 #include "base/files/file_util.h" | 24 #include "base/files/file_util.h" |
| 25 #include "base/lazy_instance.h" | 25 #include "base/lazy_instance.h" |
| 26 #include "base/location.h" | 26 #include "base/location.h" |
| 27 #include "base/logging.h" | 27 #include "base/logging.h" |
| 28 #include "base/memory/scoped_ptr.h" | 28 #include "base/memory/scoped_ptr.h" |
| 29 #include "base/message_loop/message_loop.h" | |
| 30 #include "base/message_loop/message_loop_proxy.h" | |
| 31 #include "base/posix/eintr_wrapper.h" | 29 #include "base/posix/eintr_wrapper.h" |
| 30 #include "base/single_thread_task_runner.h" |
| 32 #include "base/synchronization/lock.h" | 31 #include "base/synchronization/lock.h" |
| 32 #include "base/thread_task_runner_handle.h" |
| 33 #include "base/threading/thread.h" | 33 #include "base/threading/thread.h" |
| 34 #include "base/trace_event/trace_event.h" | 34 #include "base/trace_event/trace_event.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. |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 257 InotifyReader::InotifyReader() | 257 InotifyReader::InotifyReader() |
| 258 : thread_("inotify_reader"), | 258 : thread_("inotify_reader"), |
| 259 inotify_fd_(inotify_init()), | 259 inotify_fd_(inotify_init()), |
| 260 valid_(false) { | 260 valid_(false) { |
| 261 if (inotify_fd_ < 0) | 261 if (inotify_fd_ < 0) |
| 262 PLOG(ERROR) << "inotify_init() failed"; | 262 PLOG(ERROR) << "inotify_init() failed"; |
| 263 | 263 |
| 264 shutdown_pipe_[0] = -1; | 264 shutdown_pipe_[0] = -1; |
| 265 shutdown_pipe_[1] = -1; | 265 shutdown_pipe_[1] = -1; |
| 266 if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) { | 266 if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) { |
| 267 thread_.message_loop()->PostTask( | 267 thread_.task_runner()->PostTask( |
| 268 FROM_HERE, | 268 FROM_HERE, |
| 269 Bind(&InotifyReaderCallback, this, inotify_fd_, shutdown_pipe_[0])); | 269 Bind(&InotifyReaderCallback, this, inotify_fd_, shutdown_pipe_[0])); |
| 270 valid_ = true; | 270 valid_ = true; |
| 271 } | 271 } |
| 272 } | 272 } |
| 273 | 273 |
| 274 InotifyReader::~InotifyReader() { | 274 InotifyReader::~InotifyReader() { |
| 275 if (valid_) { | 275 if (valid_) { |
| 276 // Write to the self-pipe so that the select call in InotifyReaderTask | 276 // Write to the self-pipe so that the select call in InotifyReaderTask |
| 277 // returns. | 277 // returns. |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 342 | 342 |
| 343 FilePathWatcherImpl::FilePathWatcherImpl() | 343 FilePathWatcherImpl::FilePathWatcherImpl() |
| 344 : recursive_(false) { | 344 : recursive_(false) { |
| 345 } | 345 } |
| 346 | 346 |
| 347 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, | 347 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, |
| 348 const FilePath::StringType& child, | 348 const FilePath::StringType& child, |
| 349 bool created, | 349 bool created, |
| 350 bool deleted, | 350 bool deleted, |
| 351 bool is_dir) { | 351 bool is_dir) { |
| 352 if (!message_loop()->BelongsToCurrentThread()) { | 352 if (!task_runner()->BelongsToCurrentThread()) { |
| 353 // Switch to message_loop() to access |watches_| safely. | 353 // Switch to task_runner() to access |watches_| safely. |
| 354 message_loop()->PostTask( | 354 task_runner()->PostTask(FROM_HERE, |
| 355 FROM_HERE, | 355 Bind(&FilePathWatcherImpl::OnFilePathChanged, this, |
| 356 Bind(&FilePathWatcherImpl::OnFilePathChanged, this, | 356 fired_watch, child, created, deleted, is_dir)); |
| 357 fired_watch, child, created, deleted, is_dir)); | |
| 358 return; | 357 return; |
| 359 } | 358 } |
| 360 | 359 |
| 361 // Check to see if CancelOnMessageLoopThread() has already been called. | 360 // Check to see if CancelOnMessageLoopThread() has already been called. |
| 362 // May happen when code flow reaches here from the PostTask() above. | 361 // May happen when code flow reaches here from the PostTask() above. |
| 363 if (watches_.empty()) { | 362 if (watches_.empty()) { |
| 364 DCHECK(target_.empty()); | 363 DCHECK(target_.empty()); |
| 365 return; | 364 return; |
| 366 } | 365 } |
| 367 | 366 |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 445 callback_.Run(target_, false /* error */); | 444 callback_.Run(target_, false /* error */); |
| 446 } | 445 } |
| 447 } | 446 } |
| 448 | 447 |
| 449 bool FilePathWatcherImpl::Watch(const FilePath& path, | 448 bool FilePathWatcherImpl::Watch(const FilePath& path, |
| 450 bool recursive, | 449 bool recursive, |
| 451 const FilePathWatcher::Callback& callback) { | 450 const FilePathWatcher::Callback& callback) { |
| 452 DCHECK(target_.empty()); | 451 DCHECK(target_.empty()); |
| 453 DCHECK(MessageLoopForIO::current()); | 452 DCHECK(MessageLoopForIO::current()); |
| 454 | 453 |
| 455 set_message_loop(MessageLoopProxy::current()); | 454 set_task_runner(ThreadTaskRunnerHandle::Get()); |
| 456 callback_ = callback; | 455 callback_ = callback; |
| 457 target_ = path; | 456 target_ = path; |
| 458 recursive_ = recursive; | 457 recursive_ = recursive; |
| 459 MessageLoop::current()->AddDestructionObserver(this); | 458 MessageLoop::current()->AddDestructionObserver(this); |
| 460 | 459 |
| 461 std::vector<FilePath::StringType> comps; | 460 std::vector<FilePath::StringType> comps; |
| 462 target_.GetComponents(&comps); | 461 target_.GetComponents(&comps); |
| 463 DCHECK(!comps.empty()); | 462 DCHECK(!comps.empty()); |
| 464 for (size_t i = 1; i < comps.size(); ++i) | 463 for (size_t i = 1; i < comps.size(); ++i) |
| 465 watches_.push_back(WatchEntry(comps[i])); | 464 watches_.push_back(WatchEntry(comps[i])); |
| 466 watches_.push_back(WatchEntry(FilePath::StringType())); | 465 watches_.push_back(WatchEntry(FilePath::StringType())); |
| 467 UpdateWatches(); | 466 UpdateWatches(); |
| 468 return true; | 467 return true; |
| 469 } | 468 } |
| 470 | 469 |
| 471 void FilePathWatcherImpl::Cancel() { | 470 void FilePathWatcherImpl::Cancel() { |
| 472 if (callback_.is_null()) { | 471 if (callback_.is_null()) { |
| 473 // Watch was never called, or the message_loop() thread is already gone. | 472 // Watch was never called, or the message_loop() thread is already gone. |
| 474 set_cancelled(); | 473 set_cancelled(); |
| 475 return; | 474 return; |
| 476 } | 475 } |
| 477 | 476 |
| 478 // Switch to the message_loop() if necessary so we can access |watches_|. | 477 // Switch to the message_loop() if necessary so we can access |watches_|. |
| 479 if (!message_loop()->BelongsToCurrentThread()) { | 478 if (!task_runner()->BelongsToCurrentThread()) { |
| 480 message_loop()->PostTask(FROM_HERE, | 479 task_runner()->PostTask(FROM_HERE, Bind(&FilePathWatcher::CancelWatch, |
| 481 Bind(&FilePathWatcher::CancelWatch, | 480 make_scoped_refptr(this))); |
| 482 make_scoped_refptr(this))); | |
| 483 } else { | 481 } else { |
| 484 CancelOnMessageLoopThread(); | 482 CancelOnMessageLoopThread(); |
| 485 } | 483 } |
| 486 } | 484 } |
| 487 | 485 |
| 488 void FilePathWatcherImpl::CancelOnMessageLoopThread() { | 486 void FilePathWatcherImpl::CancelOnMessageLoopThread() { |
| 489 DCHECK(message_loop()->BelongsToCurrentThread()); | 487 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 490 set_cancelled(); | 488 set_cancelled(); |
| 491 | 489 |
| 492 if (!callback_.is_null()) { | 490 if (!callback_.is_null()) { |
| 493 MessageLoop::current()->RemoveDestructionObserver(this); | 491 MessageLoop::current()->RemoveDestructionObserver(this); |
| 494 callback_.Reset(); | 492 callback_.Reset(); |
| 495 } | 493 } |
| 496 | 494 |
| 497 for (size_t i = 0; i < watches_.size(); ++i) | 495 for (size_t i = 0; i < watches_.size(); ++i) |
| 498 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); | 496 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); |
| 499 watches_.clear(); | 497 watches_.clear(); |
| 500 target_.clear(); | 498 target_.clear(); |
| 501 | 499 |
| 502 if (recursive_) | 500 if (recursive_) |
| 503 RemoveRecursiveWatches(); | 501 RemoveRecursiveWatches(); |
| 504 } | 502 } |
| 505 | 503 |
| 506 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { | 504 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { |
| 507 CancelOnMessageLoopThread(); | 505 CancelOnMessageLoopThread(); |
| 508 } | 506 } |
| 509 | 507 |
| 510 void FilePathWatcherImpl::UpdateWatches() { | 508 void FilePathWatcherImpl::UpdateWatches() { |
| 511 // Ensure this runs on the message_loop() exclusively in order to avoid | 509 // Ensure this runs on the message_loop() exclusively in order to avoid |
| 512 // concurrency issues. | 510 // concurrency issues. |
| 513 DCHECK(message_loop()->BelongsToCurrentThread()); | 511 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 514 DCHECK(HasValidWatchVector()); | 512 DCHECK(HasValidWatchVector()); |
| 515 | 513 |
| 516 // Walk the list of watches and update them as we go. | 514 // Walk the list of watches and update them as we go. |
| 517 FilePath path(FILE_PATH_LITERAL("/")); | 515 FilePath path(FILE_PATH_LITERAL("/")); |
| 518 bool path_valid = true; | 516 bool path_valid = true; |
| 519 for (size_t i = 0; i < watches_.size(); ++i) { | 517 for (size_t i = 0; i < watches_.size(); ++i) { |
| 520 WatchEntry& watch_entry = watches_[i]; | 518 WatchEntry& watch_entry = watches_[i]; |
| 521 InotifyReader::Watch old_watch = watch_entry.watch; | 519 InotifyReader::Watch old_watch = watch_entry.watch; |
| 522 watch_entry.watch = InotifyReader::kInvalidWatch; | 520 watch_entry.watch = InotifyReader::kInvalidWatch; |
| 523 watch_entry.linkname.clear(); | 521 watch_entry.linkname.clear(); |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 683 return watches_[watches_.size() - 1].subdir.empty(); | 681 return watches_[watches_.size() - 1].subdir.empty(); |
| 684 } | 682 } |
| 685 | 683 |
| 686 } // namespace | 684 } // namespace |
| 687 | 685 |
| 688 FilePathWatcher::FilePathWatcher() { | 686 FilePathWatcher::FilePathWatcher() { |
| 689 impl_ = new FilePathWatcherImpl(); | 687 impl_ = new FilePathWatcherImpl(); |
| 690 } | 688 } |
| 691 | 689 |
| 692 } // namespace base | 690 } // namespace base |
| OLD | NEW |