Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "content/common/file_path_watcher/file_path_watcher.h" | 5 #include "content/common/file_path_watcher/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 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 73 | 73 |
| 74 // Use self-pipe trick to unblock select during shutdown. | 74 // Use self-pipe trick to unblock select during shutdown. |
| 75 int shutdown_pipe_[2]; | 75 int shutdown_pipe_[2]; |
| 76 | 76 |
| 77 // Flag set to true when startup was successful. | 77 // Flag set to true when startup was successful. |
| 78 bool valid_; | 78 bool valid_; |
| 79 | 79 |
| 80 DISALLOW_COPY_AND_ASSIGN(InotifyReader); | 80 DISALLOW_COPY_AND_ASSIGN(InotifyReader); |
| 81 }; | 81 }; |
| 82 | 82 |
| 83 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { | 83 // Deletion of the FilePathWatcher will call Cancel() to dispose of this |
| 84 // object in the right thread. In that case, |this| is already cleaned up when | |
| 85 // the dtor enters. | |
| 86 // However, it can happen that the file thread exits before Cancel() is called, | |
| 87 // and by then Cancel()'s posted Task will never execute. Therefore we observe | |
| 88 // the thread's MessageLoop and cleanup early, if necessary. | |
| 89 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, | |
| 90 public MessageLoop::DestructionObserver { | |
| 84 public: | 91 public: |
| 85 FilePathWatcherImpl(); | 92 FilePathWatcherImpl(); |
| 86 | 93 |
| 87 // Called for each event coming from the watch. |fired_watch| identifies the | 94 // Called for each event coming from the watch. |fired_watch| identifies the |
| 88 // watch that fired, |child| indicates what has changed, and is relative to | 95 // watch that fired, |child| indicates what has changed, and is relative to |
| 89 // the currently watched path for |fired_watch|. The flag |created| is true if | 96 // the currently watched path for |fired_watch|. The flag |created| is true if |
| 90 // the object appears, and |is_directory| is set when the event refers to a | 97 // the object appears, and |is_directory| is set when the event refers to a |
| 91 // directory. | 98 // directory. |
| 92 void OnFilePathChanged(InotifyReader::Watch fired_watch, | 99 void OnFilePathChanged(InotifyReader::Watch fired_watch, |
| 93 const FilePath::StringType& child, | 100 const FilePath::StringType& child, |
| 94 bool created, | 101 bool created, |
| 95 bool is_directory); | 102 bool is_directory); |
| 96 | 103 |
| 97 // Start watching |path| for changes and notify |delegate| on each change. | 104 // Start watching |path| for changes and notify |delegate| on each change. |
| 98 // Returns true if watch for |path| has been added successfully. | 105 // Returns true if watch for |path| has been added successfully. |
| 99 virtual bool Watch(const FilePath& path, | 106 virtual bool Watch(const FilePath& path, |
| 100 FilePathWatcher::Delegate* delegate, | 107 FilePathWatcher::Delegate* delegate, |
| 101 base::MessageLoopProxy* loop) OVERRIDE; | 108 base::MessageLoopProxy* loop) OVERRIDE; |
| 102 | 109 |
| 103 // Cancel the watch. This unregisters the instance with InotifyReader. | 110 // Cancel the watch. This unregisters the instance with InotifyReader. |
| 104 virtual void Cancel() OVERRIDE; | 111 virtual void Cancel() OVERRIDE; |
| 105 | 112 |
| 113 // MessageLoop::DestructionObserver overrides. | |
| 114 // This allows cleaning up when the |message_loop_| thread is | |
| 115 // shutting down. | |
| 116 virtual void WillDestroyCurrentMessageLoop(); | |
| 117 | |
| 106 private: | 118 private: |
| 107 virtual ~FilePathWatcherImpl() {} | 119 virtual ~FilePathWatcherImpl(); |
| 120 | |
| 121 // Cleans up and stops observing the |message_loop_| thread. | |
| 122 void CancelInternal(); | |
| 108 | 123 |
| 109 // Inotify watches are installed for all directory components of |target_|. A | 124 // Inotify watches are installed for all directory components of |target_|. A |
| 110 // WatchEntry instance holds the watch descriptor for a component and the | 125 // WatchEntry instance holds the watch descriptor for a component and the |
| 111 // subdirectory for that identifies the next component. | 126 // subdirectory for that identifies the next component. |
| 112 struct WatchEntry { | 127 struct WatchEntry { |
| 113 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir) | 128 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir) |
| 114 : watch_(watch), | 129 : watch_(watch), |
| 115 subdir_(subdir) {} | 130 subdir_(subdir) {} |
| 116 | 131 |
| 117 InotifyReader::Watch watch_; | 132 InotifyReader::Watch watch_; |
| (...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 356 | 371 |
| 357 bool FilePathWatcherImpl::Watch(const FilePath& path, | 372 bool FilePathWatcherImpl::Watch(const FilePath& path, |
| 358 FilePathWatcher::Delegate* delegate, | 373 FilePathWatcher::Delegate* delegate, |
| 359 base::MessageLoopProxy*) { | 374 base::MessageLoopProxy*) { |
| 360 DCHECK(target_.empty()); | 375 DCHECK(target_.empty()); |
| 361 DCHECK(MessageLoopForIO::current()); | 376 DCHECK(MessageLoopForIO::current()); |
| 362 | 377 |
| 363 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread()); | 378 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread()); |
| 364 delegate_ = delegate; | 379 delegate_ = delegate; |
| 365 target_ = path; | 380 target_ = path; |
| 381 MessageLoop::current()->AddDestructionObserver(this); | |
| 382 | |
| 366 std::vector<FilePath::StringType> comps; | 383 std::vector<FilePath::StringType> comps; |
| 367 target_.GetComponents(&comps); | 384 target_.GetComponents(&comps); |
| 368 DCHECK(!comps.empty()); | 385 DCHECK(!comps.empty()); |
| 369 for (std::vector<FilePath::StringType>::const_iterator comp(++comps.begin()); | 386 for (std::vector<FilePath::StringType>::const_iterator comp(++comps.begin()); |
| 370 comp != comps.end(); ++comp) { | 387 comp != comps.end(); ++comp) { |
| 371 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); | 388 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); |
| 372 } | 389 } |
| 373 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, | 390 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, |
| 374 FilePath::StringType())); | 391 FilePath::StringType())); |
| 375 return UpdateWatches(); | 392 return UpdateWatches(); |
| 376 } | 393 } |
| 377 | 394 |
| 395 FilePathWatcherImpl::~FilePathWatcherImpl() { | |
| 396 // This covers the case when the Task is posted on Cancel(), but is not | |
| 397 // executed during shutdown, and |this| is just Released. | |
|
Mattias Nissler (ping if slow)
2011/03/21 16:07:45
But shouldn't we then see the WillDestroyCurrentMe
Joao da Silva
2011/03/22 10:10:08
I saw a code path that actually triggers this, tho
| |
| 398 if (delegate_) | |
| 399 CancelInternal(); | |
| 400 } | |
| 401 | |
| 378 void FilePathWatcherImpl::Cancel() { | 402 void FilePathWatcherImpl::Cancel() { |
| 379 if (!message_loop().get()) { | 403 if (!delegate_) { |
| 380 // Watch was never called, so exit. | 404 // Watch was never called, or the |message_loop_| thread is already gone. |
| 381 return; | 405 return; |
| 382 } | 406 } |
| 383 | 407 |
| 384 // Switch to the message_loop_ if necessary so we can access |watches_|. | 408 // Switch to the message_loop_ if necessary so we can access |watches_|. |
| 385 if (!message_loop()->BelongsToCurrentThread()) { | 409 if (!message_loop()->BelongsToCurrentThread()) { |
| 386 message_loop()->PostTask(FROM_HERE, | 410 message_loop()->PostTask(FROM_HERE, |
| 387 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); | 411 NewRunnableMethod(this, &FilePathWatcherImpl::CancelInternal)); |
| 388 return; | 412 } else { |
| 413 CancelInternal(); | |
| 389 } | 414 } |
| 415 } | |
| 416 | |
| 417 void FilePathWatcherImpl::CancelInternal() { | |
| 418 MessageLoop::current()->RemoveDestructionObserver(this); | |
| 390 | 419 |
| 391 for (WatchVector::iterator watch_entry(watches_.begin()); | 420 for (WatchVector::iterator watch_entry(watches_.begin()); |
| 392 watch_entry != watches_.end(); ++watch_entry) { | 421 watch_entry != watches_.end(); ++watch_entry) { |
| 393 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) | 422 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) |
| 394 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this); | 423 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this); |
| 395 } | 424 } |
| 396 watches_.clear(); | 425 watches_.clear(); |
| 397 delegate_ = NULL; | 426 delegate_ = NULL; |
| 398 target_.clear(); | 427 target_.clear(); |
| 399 } | 428 } |
| 400 | 429 |
| 430 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { | |
| 431 CancelInternal(); | |
| 432 } | |
| 433 | |
| 401 bool FilePathWatcherImpl::UpdateWatches() { | 434 bool FilePathWatcherImpl::UpdateWatches() { |
| 402 // Ensure this runs on the message_loop_ exclusively in order to avoid | 435 // Ensure this runs on the message_loop_ exclusively in order to avoid |
| 403 // concurrency issues. | 436 // concurrency issues. |
| 404 DCHECK(message_loop()->BelongsToCurrentThread()); | 437 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 405 | 438 |
| 406 // Walk the list of watches and update them as we go. | 439 // Walk the list of watches and update them as we go. |
| 407 FilePath path(FILE_PATH_LITERAL("/")); | 440 FilePath path(FILE_PATH_LITERAL("/")); |
| 408 bool path_valid = true; | 441 bool path_valid = true; |
| 409 for (WatchVector::iterator watch_entry(watches_.begin()); | 442 for (WatchVector::iterator watch_entry(watches_.begin()); |
| 410 watch_entry != watches_.end(); ++watch_entry) { | 443 watch_entry != watches_.end(); ++watch_entry) { |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 425 } | 458 } |
| 426 | 459 |
| 427 return true; | 460 return true; |
| 428 } | 461 } |
| 429 | 462 |
| 430 } // namespace | 463 } // namespace |
| 431 | 464 |
| 432 FilePathWatcher::FilePathWatcher() { | 465 FilePathWatcher::FilePathWatcher() { |
| 433 impl_ = new FilePathWatcherImpl(); | 466 impl_ = new FilePathWatcherImpl(); |
| 434 } | 467 } |
| OLD | NEW |