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

Side by Side Diff: chrome/browser/file_path_watcher/file_path_watcher_win.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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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 "chrome/browser/file_path_watcher/file_path_watcher.h" 5 #include "chrome/browser/file_path_watcher/file_path_watcher.h"
6 6
7 #include "base/file_path.h" 7 #include "base/file_path.h"
8 #include "base/file_util.h" 8 #include "base/file_util.h"
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/ref_counted.h" 10 #include "base/ref_counted.h"
11 #include "base/time.h" 11 #include "base/time.h"
12 #include "base/win/object_watcher.h" 12 #include "base/win/object_watcher.h"
13 #include "content/browser/browser_thread.h"
13 14
14 namespace { 15 namespace {
15 16
16 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, 17 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
17 public base::win::ObjectWatcher::Delegate { 18 public base::win::ObjectWatcher::Delegate,
19 public MessageLoop::DestructionObserver {
18 public: 20 public:
19 FilePathWatcherImpl() : delegate_(NULL), handle_(INVALID_HANDLE_VALUE) {} 21 FilePathWatcherImpl() : delegate_(NULL), handle_(INVALID_HANDLE_VALUE) {}
20 22
21 virtual bool Watch(const FilePath& path, FilePathWatcher::Delegate* delegate); 23 virtual bool Watch(const FilePath& path, FilePathWatcher::Delegate* delegate);
22 virtual void Cancel(); 24 virtual void Cancel();
23 25
24 // Callback from MessageLoopForIO. 26 // Callback from MessageLoopForIO.
25 virtual void OnObjectSignaled(HANDLE object); 27 virtual void OnObjectSignaled(HANDLE object);
26 28
29 // MessageLoop::DestructionObserver overrides.
30 // This allows cleaning up when the File thread is shutting down.
31 virtual void WillDestroyCurrentMessageLoop();
32
27 private: 33 private:
28 virtual ~FilePathWatcherImpl(); 34 virtual ~FilePathWatcherImpl();
29 35
30 // Setup a watch handle for directory |dir|. Returns true if no fatal error 36 // Setup a watch handle for directory |dir|. Returns true if no fatal error
31 // occurs. |handle| will receive the handle value if |dir| is watchable, 37 // occurs. |handle| will receive the handle value if |dir| is watchable,
32 // otherwise INVALID_HANDLE_VALUE. 38 // otherwise INVALID_HANDLE_VALUE.
33 static bool SetupWatchHandle(const FilePath& dir, HANDLE* handle) 39 static bool SetupWatchHandle(const FilePath& dir, HANDLE* handle)
34 WARN_UNUSED_RESULT; 40 WARN_UNUSED_RESULT;
35 41
36 // (Re-)Initialize the watch handle. 42 // (Re-)Initialize the watch handle.
37 bool UpdateWatch() WARN_UNUSED_RESULT; 43 bool UpdateWatch() WARN_UNUSED_RESULT;
38 44
39 // Destroy the watch handle. 45 // Destroy the watch handle.
40 void DestroyWatch(); 46 void DestroyWatch();
41 47
48 // Start observing the destruction of the File thread.
49 void StartWatchingAndObserving();
50
42 // Delegate to notify upon changes. 51 // Delegate to notify upon changes.
43 scoped_refptr<FilePathWatcher::Delegate> delegate_; 52 scoped_refptr<FilePathWatcher::Delegate> delegate_;
44 53
45 // Path we're supposed to watch (passed to delegate). 54 // Path we're supposed to watch (passed to delegate).
46 FilePath target_; 55 FilePath target_;
47 56
48 // Handle for FindFirstChangeNotification. 57 // Handle for FindFirstChangeNotification.
49 HANDLE handle_; 58 HANDLE handle_;
50 59
51 // ObjectWatcher to watch handle_ for events. 60 // ObjectWatcher to watch handle_ for events.
(...skipping 12 matching lines...) Expand all
64 73
65 bool FilePathWatcherImpl::Watch(const FilePath& path, 74 bool FilePathWatcherImpl::Watch(const FilePath& path,
66 FilePathWatcher::Delegate* delegate) { 75 FilePathWatcher::Delegate* delegate) {
67 DCHECK(target_.value().empty()); // Can only watch one path. 76 DCHECK(target_.value().empty()); // Can only watch one path.
68 delegate_ = delegate; 77 delegate_ = delegate;
69 target_ = path; 78 target_ = path;
70 79
71 if (!UpdateWatch()) 80 if (!UpdateWatch())
72 return false; 81 return false;
73 82
74 watcher_.StartWatching(handle_, this); 83 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
Mattias Nissler (ping if slow) 2011/03/16 09:41:14 We are guaranteed to run on the file thread here.
Joao da Silva 2011/03/18 16:09:17 Done.
84 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
85 NewRunnableMethod(this,
86 &FilePathWatcherImpl::StartWatchingAndObserving));
87 } else {
88 StartWatchingAndObserving();
89 }
75 90
76 return true; 91 return true;
77 } 92 }
78 93
94 void FilePathWatcherImpl::StartWatchingAndObserving() {
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
96 MessageLoop::current()->AddDestructionObserver(this);
97 watcher_.StartWatching(handle_, this);
98 }
99
79 void FilePathWatcherImpl::Cancel() { 100 void FilePathWatcherImpl::Cancel() {
80 // Switch to the file thread if necessary so we can stop |watcher_|. 101 // Switch to the file thread if necessary so we can stop |watcher_|.
81 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { 102 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
82 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 103 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
83 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); 104 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel));
84 return; 105 return;
85 } 106 }
86 107
87 if (handle_ != INVALID_HANDLE_VALUE) 108 if (handle_ != INVALID_HANDLE_VALUE)
88 DestroyWatch(); 109 DestroyWatch();
89 } 110 }
90 111
112 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
113 if (handle_ != INVALID_HANDLE_VALUE)
114 DestroyWatch();
115 }
116
117 FilePathWatcherImpl::~FilePathWatcherImpl() {
118 // Deletion of the FilePathWatcher will call Cancel() to dispose of this
119 // object in the right thread. In that case, |this| is already cleaned up
120 // and |handle_| is INVALID_HANDLE_VALUE.
121 //
122 // However, Cancel() might have to switch threads and its Task might never be
123 // called during shutdown. In that case, either |this| gets notified that the
124 // thread is shutting down and cleans up, or the Task (that wasn't executed)
125 // is deleted and releases a handle on |this|, triggering the dtor.
126 // In either case, we are on the right thread.
127
128 if (handle_ != INVALID_HANDLE_VALUE)
129 DestroyWatch();
Mattias Nissler (ping if slow) 2011/03/16 09:41:14 Same comment as for Mac: Shouldn't we already have
Joao da Silva 2011/03/18 16:09:17 Same reply as for Mac: this might be required when
130 }
131
91 void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) { 132 void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) {
92 DCHECK(object == handle_); 133 DCHECK(object == handle_);
93 // Make sure we stay alive through the body of this function. 134 // Make sure we stay alive through the body of this function.
94 scoped_refptr<FilePathWatcherImpl> keep_alive(this); 135 scoped_refptr<FilePathWatcherImpl> keep_alive(this);
95 136
96 if (!UpdateWatch()) { 137 if (!UpdateWatch()) {
97 delegate_->OnError(); 138 delegate_->OnError();
98 return; 139 return;
99 } 140 }
100 141
(...skipping 29 matching lines...) Expand all
130 } else if (!file_exists && !last_modified_.is_null()) { 171 } else if (!file_exists && !last_modified_.is_null()) {
131 last_modified_ = base::Time(); 172 last_modified_ = base::Time();
132 delegate_->OnFilePathChanged(target_); 173 delegate_->OnFilePathChanged(target_);
133 } 174 }
134 175
135 // The watch may have been cancelled by the callback. 176 // The watch may have been cancelled by the callback.
136 if (handle_ != INVALID_HANDLE_VALUE) 177 if (handle_ != INVALID_HANDLE_VALUE)
137 watcher_.StartWatching(handle_, this); 178 watcher_.StartWatching(handle_, this);
138 } 179 }
139 180
140 FilePathWatcherImpl::~FilePathWatcherImpl() {
141 if (handle_ != INVALID_HANDLE_VALUE)
142 DestroyWatch();
143 }
144
145 // static 181 // static
146 bool FilePathWatcherImpl::SetupWatchHandle(const FilePath& dir, 182 bool FilePathWatcherImpl::SetupWatchHandle(const FilePath& dir,
147 HANDLE* handle) { 183 HANDLE* handle) {
148 *handle = FindFirstChangeNotification( 184 *handle = FindFirstChangeNotification(
149 dir.value().c_str(), 185 dir.value().c_str(),
150 false, // Don't watch subtrees 186 false, // Don't watch subtrees
151 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE | 187 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE |
152 FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | 188 FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME |
153 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY); 189 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY);
154 if (*handle != INVALID_HANDLE_VALUE) { 190 if (*handle != INVALID_HANDLE_VALUE) {
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 handle_ = temp_handle; 264 handle_ = temp_handle;
229 } 265 }
230 266
231 return true; 267 return true;
232 } 268 }
233 269
234 void FilePathWatcherImpl::DestroyWatch() { 270 void FilePathWatcherImpl::DestroyWatch() {
235 watcher_.StopWatching(); 271 watcher_.StopWatching();
236 FindCloseChangeNotification(handle_); 272 FindCloseChangeNotification(handle_);
237 handle_ = INVALID_HANDLE_VALUE; 273 handle_ = INVALID_HANDLE_VALUE;
274
275 MessageLoop::current()->RemoveDestructionObserver(this);
Mattias Nissler (ping if slow) 2011/03/16 09:41:14 Oh, so what happens when UpdateWatch is executed m
Joao da Silva 2011/03/18 16:09:17 Same mistake as on the Mac version.
276 delegate_ = NULL;
238 } 277 }
239 278
240 } // namespace 279 } // namespace
241 280
242 FilePathWatcher::FilePathWatcher() { 281 FilePathWatcher::FilePathWatcher() {
243 impl_ = new FilePathWatcherImpl(); 282 impl_ = new FilePathWatcherImpl();
244 } 283 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698