Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4016)

Unified Diff: chrome/browser/file_path_watcher/file_path_watcher_mac.cc

Issue 6697020: Fixed shutdown concurrency issues in FilePathWatcher. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698