Index: chrome/browser/file_path_watcher/file_path_watcher_mac.cc |
diff --git a/chrome/browser/file_path_watcher/file_path_watcher_mac.cc b/chrome/browser/file_path_watcher/file_path_watcher_mac.cc |
index e89cf872de3a4f77e3145095bd93f6f75968c8e1..1a4642e7c9a7d9270f00490d3448eda7df51cf0e 100644 |
--- a/chrome/browser/file_path_watcher/file_path_watcher_mac.cc |
+++ b/chrome/browser/file_path_watcher/file_path_watcher_mac.cc |
@@ -7,12 +7,12 @@ |
#include <CoreServices/CoreServices.h> |
#include <set> |
-#include "base/file_path.h" |
#include "base/file_util.h" |
#include "base/logging.h" |
#include "base/mac/scoped_cftyperef.h" |
#include "base/singleton.h" |
#include "base/time.h" |
+#include "content/browser/browser_thread.h" |
namespace { |
@@ -20,7 +20,8 @@ namespace { |
const CFAbsoluteTime kEventLatencySeconds = 0.3; |
// Mac-specific file watcher implementation based on the FSEvents API. |
-class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { |
+class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, |
+ public MessageLoop::DestructionObserver { |
public: |
FilePathWatcherImpl(); |
@@ -35,12 +36,19 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { |
virtual bool Watch(const FilePath& path, FilePathWatcher::Delegate* delegate); |
virtual void Cancel(); |
+ // MessageLoop::DestructionObserver overrides. |
+ // This allows cleaning up when the UI thread is shutting down. |
+ virtual void WillDestroyCurrentMessageLoop(); |
+ |
private: |
- virtual ~FilePathWatcherImpl() {} |
+ virtual ~FilePathWatcherImpl(); |
// Destroy the event stream. |
void DestroyEventStream(); |
+ // Start observing the destruction of the UI thread. |
+ void StartObservingUIThread(); |
+ |
// Delegate to notify upon changes. |
scoped_refptr<FilePathWatcher::Delegate> delegate_; |
@@ -104,6 +112,19 @@ FilePathWatcherImpl::FilePathWatcherImpl() |
canceled_(false) { |
} |
+FilePathWatcherImpl::~FilePathWatcherImpl() { |
+ // Deletion of the FilePathWatcher will call Cancel() to dispose of this |
+ // object in the right thread. In that case, |this| is already cleaned up. |
+ // |
+ // 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) |
Bernhard Bauer
2011/03/15 22:00:07
I guess the first case refers to WillDestroyCurren
Joao da Silva
2011/03/18 16:09:17
Done.
|
+ // is deleted and releases a handle on |this|, triggering the dtor. |
+ // In either case, we are on the right thread. |
+ |
+ DestroyEventStream(); |
Mattias Nissler (ping if slow)
2011/03/16 09:41:14
If I understand correctly, the DestroyEventStream(
Joao da Silva
2011/03/18 16:09:17
This covers the case when Cancel() tries to switch
|
+} |
+ |
void FilePathWatcherImpl::OnFilePathChanged() { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
DCHECK(!target_.empty()); |
@@ -157,6 +178,11 @@ bool FilePathWatcherImpl::Watch(const FilePath& path, |
first_notification_ = base::Time::Now(); |
} |
+ // Start observing for destruction of the UI thread before starting to |
+ // receive FSEventStream events. |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ NewRunnableMethod(this, &FilePathWatcherImpl::StartObservingUIThread)); |
+ |
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
NewRunnableMethod(this, &FilePathWatcherImpl::UpdateEventStream, |
start_event)); |
@@ -172,9 +198,19 @@ void FilePathWatcherImpl::Cancel() { |
return; |
} |
- canceled_ = true; |
- if (fsevent_stream_) |
- DestroyEventStream(); |
+ DestroyEventStream(); |
+} |
+ |
+void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { |
+ // This should only execute on the UI thread, but DCHECK'ing |
+ // BrowserThread::CurrentlyOn(BrowserThread::UI) won't work because |
+ // BrowserThread::message_loop() might be returning NULL during shutdown. |
+ DestroyEventStream(); |
+} |
+ |
+void FilePathWatcherImpl::StartObservingUIThread() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ MessageLoop::current()->AddDestructionObserver(this); |
} |
void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) { |
@@ -219,12 +255,21 @@ void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) { |
} |
void FilePathWatcherImpl::DestroyEventStream() { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- FSEventStreamStop(fsevent_stream_); |
- FSEventStreamUnscheduleFromRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), |
- kCFRunLoopDefaultMode); |
- FSEventStreamRelease(fsevent_stream_); |
- fsevent_stream_ = NULL; |
+ if (canceled_) |
+ return; |
+ |
+ canceled_ = true; |
Mattias Nissler (ping if slow)
2011/03/16 09:41:14
Hm, I think we have a problem here. DestroyEventSt
Joao da Silva
2011/03/18 16:09:17
Horrible mistake, thanks for spotting. The try bot
|
+ |
+ if (fsevent_stream_) { |
+ FSEventStreamStop(fsevent_stream_); |
+ FSEventStreamUnscheduleFromRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), |
+ kCFRunLoopDefaultMode); |
+ FSEventStreamRelease(fsevent_stream_); |
+ fsevent_stream_ = NULL; |
+ |
+ MessageLoop::current()->RemoveDestructionObserver(this); |
Mattias Nissler (ping if slow)
2011/03/16 09:41:14
Same problem as above, this will kill our destruct
|
+ delegate_ = NULL; |
+ } |
} |
} // namespace |