| 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 14 matching lines...) Expand all Loading... |
| 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/posix/eintr_wrapper.h" | 31 #include "base/posix/eintr_wrapper.h" |
| 32 #include "base/single_thread_task_runner.h" | 32 #include "base/single_thread_task_runner.h" |
| 33 #include "base/stl_util.h" | 33 #include "base/stl_util.h" |
| 34 #include "base/synchronization/lock.h" | 34 #include "base/synchronization/lock.h" |
| 35 #include "base/threading/sequenced_task_runner_handle.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/trace_event/trace_event.h" | 37 #include "base/trace_event/trace_event.h" |
| 38 | 38 |
| 39 namespace base { | 39 namespace base { |
| 40 | 40 |
| 41 namespace { | 41 namespace { |
| 42 | 42 |
| 43 class FilePathWatcherImpl; | 43 class FilePathWatcherImpl; |
| 44 | 44 |
| 45 // Singleton to manage all inotify watches. | 45 // Singleton to manage all inotify watches. |
| 46 // TODO(tony): It would be nice if this wasn't a singleton. | 46 // TODO(tony): It would be nice if this wasn't a singleton. |
| (...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 313 | 313 |
| 314 FilePathWatcherImpl::FilePathWatcherImpl() | 314 FilePathWatcherImpl::FilePathWatcherImpl() |
| 315 : recursive_(false) { | 315 : recursive_(false) { |
| 316 } | 316 } |
| 317 | 317 |
| 318 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, | 318 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, |
| 319 const FilePath::StringType& child, | 319 const FilePath::StringType& child, |
| 320 bool created, | 320 bool created, |
| 321 bool deleted, | 321 bool deleted, |
| 322 bool is_dir) { | 322 bool is_dir) { |
| 323 if (!task_runner()->BelongsToCurrentThread()) { | 323 if (!task_runner()->RunsTasksOnCurrentThread()) { |
| 324 // Switch to task_runner() to access |watches_| safely. | 324 // Switch to task_runner() to access |watches_| safely. |
| 325 task_runner()->PostTask(FROM_HERE, | 325 task_runner()->PostTask(FROM_HERE, |
| 326 Bind(&FilePathWatcherImpl::OnFilePathChanged, this, | 326 Bind(&FilePathWatcherImpl::OnFilePathChanged, this, |
| 327 fired_watch, child, created, deleted, is_dir)); | 327 fired_watch, child, created, deleted, is_dir)); |
| 328 return; | 328 return; |
| 329 } | 329 } |
| 330 | 330 |
| 331 // Check to see if CancelOnMessageLoopThreadOrInDestructor() has already been | 331 // Check to see if CancelOnMessageLoopThreadOrInDestructor() has already been |
| 332 // called. May happen when code flow reaches here from the PostTask() above. | 332 // called. May happen when code flow reaches here from the PostTask() above. |
| 333 if (watches_.empty()) { | 333 if (watches_.empty()) { |
| 334 DCHECK(target_.empty()); | 334 DCHECK(target_.empty()); |
| 335 return; | 335 return; |
| 336 } | 336 } |
| 337 | 337 |
| 338 DCHECK(task_runner()->BelongsToCurrentThread()); | 338 DCHECK(task_runner()->RunsTasksOnCurrentThread()); |
| 339 DCHECK(HasValidWatchVector()); | 339 DCHECK(HasValidWatchVector()); |
| 340 | 340 |
| 341 // Used below to avoid multiple recursive updates. | 341 // Used below to avoid multiple recursive updates. |
| 342 bool did_update = false; | 342 bool did_update = false; |
| 343 | 343 |
| 344 // Find the entry in |watches_| that corresponds to |fired_watch|. | 344 // Find the entry in |watches_| that corresponds to |fired_watch|. |
| 345 for (size_t i = 0; i < watches_.size(); ++i) { | 345 for (size_t i = 0; i < watches_.size(); ++i) { |
| 346 const WatchEntry& watch_entry = watches_[i]; | 346 const WatchEntry& watch_entry = watches_[i]; |
| 347 if (fired_watch != watch_entry.watch) | 347 if (fired_watch != watch_entry.watch) |
| 348 continue; | 348 continue; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 414 UpdateRecursiveWatches(fired_watch, is_dir); | 414 UpdateRecursiveWatches(fired_watch, is_dir); |
| 415 callback_.Run(target_, false /* error */); | 415 callback_.Run(target_, false /* error */); |
| 416 } | 416 } |
| 417 } | 417 } |
| 418 | 418 |
| 419 bool FilePathWatcherImpl::Watch(const FilePath& path, | 419 bool FilePathWatcherImpl::Watch(const FilePath& path, |
| 420 bool recursive, | 420 bool recursive, |
| 421 const FilePathWatcher::Callback& callback) { | 421 const FilePathWatcher::Callback& callback) { |
| 422 DCHECK(target_.empty()); | 422 DCHECK(target_.empty()); |
| 423 | 423 |
| 424 set_task_runner(ThreadTaskRunnerHandle::Get()); | 424 set_task_runner(SequencedTaskRunnerHandle::Get()); |
| 425 callback_ = callback; | 425 callback_ = callback; |
| 426 target_ = path; | 426 target_ = path; |
| 427 recursive_ = recursive; | 427 recursive_ = recursive; |
| 428 | 428 |
| 429 std::vector<FilePath::StringType> comps; | 429 std::vector<FilePath::StringType> comps; |
| 430 target_.GetComponents(&comps); | 430 target_.GetComponents(&comps); |
| 431 DCHECK(!comps.empty()); | 431 DCHECK(!comps.empty()); |
| 432 for (size_t i = 1; i < comps.size(); ++i) | 432 for (size_t i = 1; i < comps.size(); ++i) |
| 433 watches_.push_back(WatchEntry(comps[i])); | 433 watches_.push_back(WatchEntry(comps[i])); |
| 434 watches_.push_back(WatchEntry(FilePath::StringType())); | 434 watches_.push_back(WatchEntry(FilePath::StringType())); |
| 435 UpdateWatches(); | 435 UpdateWatches(); |
| 436 return true; | 436 return true; |
| 437 } | 437 } |
| 438 | 438 |
| 439 void FilePathWatcherImpl::Cancel() { | 439 void FilePathWatcherImpl::Cancel() { |
| 440 if (callback_.is_null()) { | 440 if (callback_.is_null()) { |
| 441 // Watch was never called, or the message_loop() thread is already gone. | 441 // Watch was never called, or the message_loop() thread is already gone. |
| 442 set_cancelled(); | 442 set_cancelled(); |
| 443 return; | 443 return; |
| 444 } | 444 } |
| 445 | 445 |
| 446 // Switch to the task_runner() if necessary so we can access |watches_|. | 446 // Switch to the task_runner() if necessary so we can access |watches_|. |
| 447 if (!task_runner()->BelongsToCurrentThread()) { | 447 if (!task_runner()->RunsTasksOnCurrentThread()) { |
| 448 task_runner()->PostTask( | 448 task_runner()->PostTask( |
| 449 FROM_HERE, | 449 FROM_HERE, |
| 450 Bind(&FilePathWatcherImpl::CancelOnMessageLoopThreadOrInDestructor, | 450 Bind(&FilePathWatcherImpl::CancelOnMessageLoopThreadOrInDestructor, |
| 451 this)); | 451 this)); |
| 452 } else { | 452 } else { |
| 453 CancelOnMessageLoopThreadOrInDestructor(); | 453 CancelOnMessageLoopThreadOrInDestructor(); |
| 454 } | 454 } |
| 455 } | 455 } |
| 456 | 456 |
| 457 void FilePathWatcherImpl::CancelOnMessageLoopThreadOrInDestructor() { | 457 void FilePathWatcherImpl::CancelOnMessageLoopThreadOrInDestructor() { |
| 458 DCHECK(in_destructor_ || task_runner()->BelongsToCurrentThread()); | 458 DCHECK(in_destructor_ || task_runner()->RunsTasksOnCurrentThread()); |
| 459 | 459 |
| 460 if (is_cancelled()) | 460 if (is_cancelled()) |
| 461 return; | 461 return; |
| 462 | 462 |
| 463 set_cancelled(); | 463 set_cancelled(); |
| 464 | 464 |
| 465 if (!callback_.is_null()) | 465 if (!callback_.is_null()) |
| 466 callback_.Reset(); | 466 callback_.Reset(); |
| 467 | 467 |
| 468 for (size_t i = 0; i < watches_.size(); ++i) | 468 for (size_t i = 0; i < watches_.size(); ++i) |
| 469 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); | 469 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); |
| 470 watches_.clear(); | 470 watches_.clear(); |
| 471 target_.clear(); | 471 target_.clear(); |
| 472 | 472 |
| 473 if (recursive_) | 473 if (recursive_) |
| 474 RemoveRecursiveWatches(); | 474 RemoveRecursiveWatches(); |
| 475 } | 475 } |
| 476 | 476 |
| 477 void FilePathWatcherImpl::UpdateWatches() { | 477 void FilePathWatcherImpl::UpdateWatches() { |
| 478 // Ensure this runs on the task_runner() exclusively in order to avoid | 478 // Ensure this runs on the task_runner() exclusively in order to avoid |
| 479 // concurrency issues. | 479 // concurrency issues. |
| 480 DCHECK(task_runner()->BelongsToCurrentThread()); | 480 DCHECK(task_runner()->RunsTasksOnCurrentThread()); |
| 481 DCHECK(HasValidWatchVector()); | 481 DCHECK(HasValidWatchVector()); |
| 482 | 482 |
| 483 // Walk the list of watches and update them as we go. | 483 // Walk the list of watches and update them as we go. |
| 484 FilePath path(FILE_PATH_LITERAL("/")); | 484 FilePath path(FILE_PATH_LITERAL("/")); |
| 485 for (size_t i = 0; i < watches_.size(); ++i) { | 485 for (size_t i = 0; i < watches_.size(); ++i) { |
| 486 WatchEntry& watch_entry = watches_[i]; | 486 WatchEntry& watch_entry = watches_[i]; |
| 487 InotifyReader::Watch old_watch = watch_entry.watch; | 487 InotifyReader::Watch old_watch = watch_entry.watch; |
| 488 watch_entry.watch = InotifyReader::kInvalidWatch; | 488 watch_entry.watch = InotifyReader::kInvalidWatch; |
| 489 watch_entry.linkname.clear(); | 489 watch_entry.linkname.clear(); |
| 490 watch_entry.watch = g_inotify_reader.Get().AddWatch(path, this); | 490 watch_entry.watch = g_inotify_reader.Get().AddWatch(path, this); |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 654 } | 654 } |
| 655 | 655 |
| 656 } // namespace | 656 } // namespace |
| 657 | 657 |
| 658 FilePathWatcher::FilePathWatcher() { | 658 FilePathWatcher::FilePathWatcher() { |
| 659 sequence_checker_.DetachFromSequence(); | 659 sequence_checker_.DetachFromSequence(); |
| 660 impl_ = new FilePathWatcherImpl(); | 660 impl_ = new FilePathWatcherImpl(); |
| 661 } | 661 } |
| 662 | 662 |
| 663 } // namespace base | 663 } // namespace base |
| OLD | NEW |