Chromium Code Reviews| Index: content/common/file_path_watcher/file_path_watcher_mac.cc |
| diff --git a/content/common/file_path_watcher/file_path_watcher_mac.cc b/content/common/file_path_watcher/file_path_watcher_mac.cc |
| index 6e89ad1a6b588d792fdd1e8132c6960e4bc4e3d4..ea201457c2081abe4d2062cce9d64701de5b18cd 100644 |
| --- a/content/common/file_path_watcher/file_path_watcher_mac.cc |
| +++ b/content/common/file_path_watcher/file_path_watcher_mac.cc |
| @@ -32,7 +32,18 @@ namespace { |
| const CFAbsoluteTime kEventLatencySeconds = 0.3; |
| // Mac-specific file watcher implementation based on the FSEvents API. |
| -class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { |
| +// |
| +// Deletion of the FilePathWatcher will call Cancel() to dispose of this |
| +// object in the right thread. In that case, |this| is already cleaned up when |
| +// the dtor enters. |
| +// |
| +// However, Cancel() might have to switch threads and its Task might never be |
| +// called during shutdown. In that case, either |this| gets notified that the |
| +// thread is shutting down and cleans up, or the Task (that wasn't executed) |
| +// is deleted and releases a handle on |this|, triggering the dtor. |
| +// In either case, DestroyEventStream() is called on the right thread. |
|
Mattias Nissler (ping if slow)
2011/03/21 16:07:45
Does the second case actually happen? I would expe
Joao da Silva
2011/03/22 10:10:08
It does happen, see previous comment.
|
| +class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, |
| + public MessageLoop::DestructionObserver { |
| public: |
| FilePathWatcherImpl(); |
| @@ -49,16 +60,28 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { |
| base::MessageLoopProxy* loop) OVERRIDE; |
| virtual void Cancel() OVERRIDE; |
| + // MessageLoop::DestructionObserver overrides. |
| + // This allows cleaning up when the |run_loop_message_loop_| thread is |
| + // shutting down. |
| + virtual void WillDestroyCurrentMessageLoop(); |
| + |
| scoped_refptr<base::MessageLoopProxy> run_loop_message_loop() { |
| return run_loop_message_loop_; |
| } |
| private: |
| - virtual ~FilePathWatcherImpl() {} |
| + virtual ~FilePathWatcherImpl(); |
| // Destroy the event stream. |
| void DestroyEventStream(); |
| + // Start observing the destruction of the |run_loop_message_loop_| thread, |
| + // and watching the FSEventStream. |
| + void StartObserverAndEventStream(FSEventStreamEventId start_event); |
| + |
| + // Cleans up and stops observing the |run_loop_message_loop_| thread. |
| + void CancelInternal(); |
| + |
| // Delegate to notify upon changes. |
| scoped_refptr<FilePathWatcher::Delegate> delegate_; |
| @@ -191,12 +214,24 @@ bool FilePathWatcherImpl::Watch(const FilePath& path, |
| } |
| run_loop_message_loop()->PostTask(FROM_HERE, |
| - NewRunnableMethod(this, &FilePathWatcherImpl::UpdateEventStream, |
| + NewRunnableMethod(this, &FilePathWatcherImpl::StartObserverAndEventStream, |
| start_event)); |
| return true; |
| } |
| +void FilePathWatcherImpl::StartObserverAndEventStream( |
| + FSEventStreamEventId start_event) { |
| + DCHECK(run_loop_message_loop()->BelongsToCurrentThread()); |
| + MessageLoop::current()->AddDestructionObserver(this); |
| + UpdateEventStream(start_event); |
| +} |
| + |
| +FilePathWatcherImpl::~FilePathWatcherImpl() { |
| + if (!canceled_) |
| + CancelInternal(); |
| +} |
| + |
| void FilePathWatcherImpl::Cancel() { |
| if (!run_loop_message_loop().get()) { |
| // Watch was never called, so exit. |
| @@ -207,13 +242,28 @@ void FilePathWatcherImpl::Cancel() { |
| // the event stream. |
| if (!run_loop_message_loop()->BelongsToCurrentThread()) { |
| run_loop_message_loop()->PostTask(FROM_HERE, |
| - NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); |
| - return; |
| + NewRunnableMethod(this, &FilePathWatcherImpl::CancelInternal)); |
| + } else { |
| + CancelInternal(); |
| } |
| +} |
| +void FilePathWatcherImpl::CancelInternal() { |
| canceled_ = true; |
| - if (fsevent_stream_) |
| + if (fsevent_stream_) { |
| DestroyEventStream(); |
| + MessageLoop::current()->RemoveDestructionObserver(this); |
| + delegate_ = NULL; |
| + } |
| +} |
| + |
| +void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { |
| + // This DCHECK will fail because the |run_loop_message_loop_| has been |
| + // notified that the thread is quitting too. |
| + // |
| + // DCHECK(run_loop_message_loop()->BelongsToCurrentThread()); |
|
Mattias Nissler (ping if slow)
2011/03/21 16:07:45
So just remove this block?
Joao da Silva
2011/03/22 10:10:08
Done.
|
| + |
| + CancelInternal(); |
| } |
| void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) { |
| @@ -259,7 +309,6 @@ void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) { |
| } |
| void FilePathWatcherImpl::DestroyEventStream() { |
| - DCHECK(run_loop_message_loop()->BelongsToCurrentThread()); |
| FSEventStreamStop(fsevent_stream_); |
| FSEventStreamUnscheduleFromRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), |
| kCFRunLoopDefaultMode); |