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

Side by Side Diff: content/common/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: Tested and updated after changes from 6670081 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) 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 <CoreServices/CoreServices.h> 7 #include <CoreServices/CoreServices.h>
8 #include <set> 8 #include <set>
9 9
10 #include "base/file_path.h" 10 #include "base/file_path.h"
(...skipping 14 matching lines...) Expand all
25 // resource. If you have a good idea on how to get around this, the source for a 25 // resource. If you have a good idea on how to get around this, the source for a
26 // reasonable implementation of this class using kqueues is attached here: 26 // reasonable implementation of this class using kqueues is attached here:
27 // http://code.google.com/p/chromium/issues/detail?id=54822#c13 27 // http://code.google.com/p/chromium/issues/detail?id=54822#c13
28 28
29 namespace { 29 namespace {
30 30
31 // The latency parameter passed to FSEventsStreamCreate(). 31 // The latency parameter passed to FSEventsStreamCreate().
32 const CFAbsoluteTime kEventLatencySeconds = 0.3; 32 const CFAbsoluteTime kEventLatencySeconds = 0.3;
33 33
34 // Mac-specific file watcher implementation based on the FSEvents API. 34 // Mac-specific file watcher implementation based on the FSEvents API.
35 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { 35 //
36 // Deletion of the FilePathWatcher will call Cancel() to dispose of this
37 // object in the right thread. In that case, |this| is already cleaned up when
38 // the dtor enters.
39 //
40 // However, Cancel() might have to switch threads and its Task might never be
41 // called during shutdown. In that case, either |this| gets notified that the
42 // thread is shutting down and cleans up, or the Task (that wasn't executed)
43 // is deleted and releases a handle on |this|, triggering the dtor.
44 // 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.
45 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
46 public MessageLoop::DestructionObserver {
36 public: 47 public:
37 FilePathWatcherImpl(); 48 FilePathWatcherImpl();
38 49
39 // Called from the FSEvents callback whenever there is a change to the paths 50 // Called from the FSEvents callback whenever there is a change to the paths
40 void OnFilePathChanged(); 51 void OnFilePathChanged();
41 52
42 // (Re-)Initialize the event stream to start reporting events from 53 // (Re-)Initialize the event stream to start reporting events from
43 // |start_event|. 54 // |start_event|.
44 void UpdateEventStream(FSEventStreamEventId start_event); 55 void UpdateEventStream(FSEventStreamEventId start_event);
45 56
46 // FilePathWatcher::PlatformDelegate overrides. 57 // FilePathWatcher::PlatformDelegate overrides.
47 virtual bool Watch(const FilePath& path, 58 virtual bool Watch(const FilePath& path,
48 FilePathWatcher::Delegate* delegate, 59 FilePathWatcher::Delegate* delegate,
49 base::MessageLoopProxy* loop) OVERRIDE; 60 base::MessageLoopProxy* loop) OVERRIDE;
50 virtual void Cancel() OVERRIDE; 61 virtual void Cancel() OVERRIDE;
51 62
63 // MessageLoop::DestructionObserver overrides.
64 // This allows cleaning up when the |run_loop_message_loop_| thread is
65 // shutting down.
66 virtual void WillDestroyCurrentMessageLoop();
67
52 scoped_refptr<base::MessageLoopProxy> run_loop_message_loop() { 68 scoped_refptr<base::MessageLoopProxy> run_loop_message_loop() {
53 return run_loop_message_loop_; 69 return run_loop_message_loop_;
54 } 70 }
55 71
56 private: 72 private:
57 virtual ~FilePathWatcherImpl() {} 73 virtual ~FilePathWatcherImpl();
58 74
59 // Destroy the event stream. 75 // Destroy the event stream.
60 void DestroyEventStream(); 76 void DestroyEventStream();
61 77
78 // Start observing the destruction of the |run_loop_message_loop_| thread,
79 // and watching the FSEventStream.
80 void StartObserverAndEventStream(FSEventStreamEventId start_event);
81
82 // Cleans up and stops observing the |run_loop_message_loop_| thread.
83 void CancelInternal();
84
62 // Delegate to notify upon changes. 85 // Delegate to notify upon changes.
63 scoped_refptr<FilePathWatcher::Delegate> delegate_; 86 scoped_refptr<FilePathWatcher::Delegate> delegate_;
64 87
65 // Target path to watch (passed to delegate). 88 // Target path to watch (passed to delegate).
66 FilePath target_; 89 FilePath target_;
67 90
68 // Keep track of the last modified time of the file. We use nulltime 91 // Keep track of the last modified time of the file. We use nulltime
69 // to represent the file not existing. 92 // to represent the file not existing.
70 base::Time last_modified_; 93 base::Time last_modified_;
71 94
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 207
185 FSEventStreamEventId start_event = FSEventsGetCurrentEventId(); 208 FSEventStreamEventId start_event = FSEventsGetCurrentEventId();
186 209
187 base::PlatformFileInfo file_info; 210 base::PlatformFileInfo file_info;
188 if (file_util::GetFileInfo(target_, &file_info)) { 211 if (file_util::GetFileInfo(target_, &file_info)) {
189 last_modified_ = file_info.last_modified; 212 last_modified_ = file_info.last_modified;
190 first_notification_ = base::Time::Now(); 213 first_notification_ = base::Time::Now();
191 } 214 }
192 215
193 run_loop_message_loop()->PostTask(FROM_HERE, 216 run_loop_message_loop()->PostTask(FROM_HERE,
194 NewRunnableMethod(this, &FilePathWatcherImpl::UpdateEventStream, 217 NewRunnableMethod(this, &FilePathWatcherImpl::StartObserverAndEventStream,
195 start_event)); 218 start_event));
196 219
197 return true; 220 return true;
198 } 221 }
199 222
223 void FilePathWatcherImpl::StartObserverAndEventStream(
224 FSEventStreamEventId start_event) {
225 DCHECK(run_loop_message_loop()->BelongsToCurrentThread());
226 MessageLoop::current()->AddDestructionObserver(this);
227 UpdateEventStream(start_event);
228 }
229
230 FilePathWatcherImpl::~FilePathWatcherImpl() {
231 if (!canceled_)
232 CancelInternal();
233 }
234
200 void FilePathWatcherImpl::Cancel() { 235 void FilePathWatcherImpl::Cancel() {
201 if (!run_loop_message_loop().get()) { 236 if (!run_loop_message_loop().get()) {
202 // Watch was never called, so exit. 237 // Watch was never called, so exit.
203 return; 238 return;
204 } 239 }
205 240
206 // Switch to the CFRunLoop based thread if necessary, so we can tear down 241 // Switch to the CFRunLoop based thread if necessary, so we can tear down
207 // the event stream. 242 // the event stream.
208 if (!run_loop_message_loop()->BelongsToCurrentThread()) { 243 if (!run_loop_message_loop()->BelongsToCurrentThread()) {
209 run_loop_message_loop()->PostTask(FROM_HERE, 244 run_loop_message_loop()->PostTask(FROM_HERE,
210 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); 245 NewRunnableMethod(this, &FilePathWatcherImpl::CancelInternal));
211 return; 246 } else {
247 CancelInternal();
212 } 248 }
249 }
213 250
251 void FilePathWatcherImpl::CancelInternal() {
214 canceled_ = true; 252 canceled_ = true;
215 if (fsevent_stream_) 253 if (fsevent_stream_) {
216 DestroyEventStream(); 254 DestroyEventStream();
255 MessageLoop::current()->RemoveDestructionObserver(this);
256 delegate_ = NULL;
257 }
258 }
259
260 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
261 // This DCHECK will fail because the |run_loop_message_loop_| has been
262 // notified that the thread is quitting too.
263 //
264 // 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.
265
266 CancelInternal();
217 } 267 }
218 268
219 void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) { 269 void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) {
220 DCHECK(run_loop_message_loop()->BelongsToCurrentThread()); 270 DCHECK(run_loop_message_loop()->BelongsToCurrentThread());
221 DCHECK(MessageLoopForUI::current()); 271 DCHECK(MessageLoopForUI::current());
222 272
223 // It can happen that the watcher gets canceled while tasks that call this 273 // It can happen that the watcher gets canceled while tasks that call this
224 // function are still in flight, so abort if this situation is detected. 274 // function are still in flight, so abort if this situation is detected.
225 if (canceled_) 275 if (canceled_)
226 return; 276 return;
(...skipping 25 matching lines...) Expand all
252 FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), 302 FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(),
253 kCFRunLoopDefaultMode); 303 kCFRunLoopDefaultMode);
254 if (!FSEventStreamStart(fsevent_stream_)) { 304 if (!FSEventStreamStart(fsevent_stream_)) {
255 message_loop()->PostTask(FROM_HERE, 305 message_loop()->PostTask(FROM_HERE,
256 NewRunnableMethod(delegate_.get(), 306 NewRunnableMethod(delegate_.get(),
257 &FilePathWatcher::Delegate::OnError)); 307 &FilePathWatcher::Delegate::OnError));
258 } 308 }
259 } 309 }
260 310
261 void FilePathWatcherImpl::DestroyEventStream() { 311 void FilePathWatcherImpl::DestroyEventStream() {
262 DCHECK(run_loop_message_loop()->BelongsToCurrentThread());
263 FSEventStreamStop(fsevent_stream_); 312 FSEventStreamStop(fsevent_stream_);
264 FSEventStreamUnscheduleFromRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), 313 FSEventStreamUnscheduleFromRunLoop(fsevent_stream_, CFRunLoopGetCurrent(),
265 kCFRunLoopDefaultMode); 314 kCFRunLoopDefaultMode);
266 FSEventStreamRelease(fsevent_stream_); 315 FSEventStreamRelease(fsevent_stream_);
267 fsevent_stream_ = NULL; 316 fsevent_stream_ = NULL;
268 } 317 }
269 318
270 } // namespace 319 } // namespace
271 320
272 FilePathWatcher::FilePathWatcher() { 321 FilePathWatcher::FilePathWatcher() {
273 impl_ = new FilePathWatcherImpl(); 322 impl_ = new FilePathWatcherImpl();
274 } 323 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698