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

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: Introduced CancelTask to simplify cleanup. 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 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
36 public MessageLoop::DestructionObserver {
36 public: 37 public:
37 FilePathWatcherImpl(); 38 FilePathWatcherImpl();
38 39
39 // Called from the FSEvents callback whenever there is a change to the paths 40 // Called from the FSEvents callback whenever there is a change to the paths
40 void OnFilePathChanged(); 41 void OnFilePathChanged();
41 42
42 // (Re-)Initialize the event stream to start reporting events from 43 // (Re-)Initialize the event stream to start reporting events from
43 // |start_event|. 44 // |start_event|.
44 void UpdateEventStream(FSEventStreamEventId start_event); 45 void UpdateEventStream(FSEventStreamEventId start_event);
45 46
46 // FilePathWatcher::PlatformDelegate overrides. 47 // FilePathWatcher::PlatformDelegate overrides.
47 virtual bool Watch(const FilePath& path, 48 virtual bool Watch(const FilePath& path,
48 FilePathWatcher::Delegate* delegate, 49 FilePathWatcher::Delegate* delegate,
49 base::MessageLoopProxy* loop) OVERRIDE; 50 base::MessageLoopProxy* loop) OVERRIDE;
50 virtual void Cancel() OVERRIDE; 51 virtual void Cancel() OVERRIDE;
51 52
53 // Deletion of the FilePathWatcher will call Cancel() to dispose of this
54 // object in the right thread. This also observes destruction of the required
55 // cleanup thread, in case it quits before Cancel() is called.
56 virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
57
52 scoped_refptr<base::MessageLoopProxy> run_loop_message_loop() { 58 scoped_refptr<base::MessageLoopProxy> run_loop_message_loop() {
53 return run_loop_message_loop_; 59 return run_loop_message_loop_;
54 } 60 }
55 61
56 private: 62 private:
57 virtual ~FilePathWatcherImpl() {} 63 virtual ~FilePathWatcherImpl() {}
58 64
59 // Destroy the event stream. 65 // Destroy the event stream.
60 void DestroyEventStream(); 66 void DestroyEventStream();
61 67
68 // Start observing the destruction of the |run_loop_message_loop_| thread,
69 // and watching the FSEventStream.
70 void StartObserverAndEventStream(FSEventStreamEventId start_event);
71
72 // Cleans up and stops observing the |run_loop_message_loop_| thread.
73 void CancelOnMessageLoopThread() OVERRIDE;
74
62 // Delegate to notify upon changes. 75 // Delegate to notify upon changes.
63 scoped_refptr<FilePathWatcher::Delegate> delegate_; 76 scoped_refptr<FilePathWatcher::Delegate> delegate_;
64 77
65 // Target path to watch (passed to delegate). 78 // Target path to watch (passed to delegate).
66 FilePath target_; 79 FilePath target_;
67 80
68 // Keep track of the last modified time of the file. We use nulltime 81 // Keep track of the last modified time of the file. We use nulltime
69 // to represent the file not existing. 82 // to represent the file not existing.
70 base::Time last_modified_; 83 base::Time last_modified_;
71 84
72 // The time at which we processed the first notification with the 85 // The time at which we processed the first notification with the
73 // |last_modified_| time stamp. 86 // |last_modified_| time stamp.
74 base::Time first_notification_; 87 base::Time first_notification_;
75 88
76 // Backend stream we receive event callbacks from (strong reference). 89 // Backend stream we receive event callbacks from (strong reference).
77 FSEventStreamRef fsevent_stream_; 90 FSEventStreamRef fsevent_stream_;
78 91
79 // Run loop for FSEventStream to run on. 92 // Run loop for FSEventStream to run on.
80 scoped_refptr<base::MessageLoopProxy> run_loop_message_loop_; 93 scoped_refptr<base::MessageLoopProxy> run_loop_message_loop_;
81 94
82 // Used to detect early cancellation.
83 bool canceled_;
84
85 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); 95 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
86 }; 96 };
87 97
88 // The callback passed to FSEventStreamCreate(). 98 // The callback passed to FSEventStreamCreate().
89 void FSEventsCallback(ConstFSEventStreamRef stream, 99 void FSEventsCallback(ConstFSEventStreamRef stream,
90 void* event_watcher, size_t num_events, 100 void* event_watcher, size_t num_events,
91 void* event_paths, const FSEventStreamEventFlags flags[], 101 void* event_paths, const FSEventStreamEventFlags flags[],
92 const FSEventStreamEventId event_ids[]) { 102 const FSEventStreamEventId event_ids[]) {
93 FilePathWatcherImpl* watcher = 103 FilePathWatcherImpl* watcher =
94 reinterpret_cast<FilePathWatcherImpl*>(event_watcher); 104 reinterpret_cast<FilePathWatcherImpl*>(event_watcher);
(...skipping 18 matching lines...) Expand all
113 NewRunnableMethod(watcher, &FilePathWatcherImpl::UpdateEventStream, 123 NewRunnableMethod(watcher, &FilePathWatcherImpl::UpdateEventStream,
114 root_change_at)); 124 root_change_at));
115 } 125 }
116 126
117 watcher->OnFilePathChanged(); 127 watcher->OnFilePathChanged();
118 } 128 }
119 129
120 // FilePathWatcherImpl implementation: 130 // FilePathWatcherImpl implementation:
121 131
122 FilePathWatcherImpl::FilePathWatcherImpl() 132 FilePathWatcherImpl::FilePathWatcherImpl()
123 : fsevent_stream_(NULL), 133 : fsevent_stream_(NULL) {
124 canceled_(false) {
125 } 134 }
126 135
127 void FilePathWatcherImpl::OnFilePathChanged() { 136 void FilePathWatcherImpl::OnFilePathChanged() {
128 // Switch to the CFRunLoop based thread if necessary, so we can tear down 137 // Switch to the CFRunLoop based thread if necessary, so we can tear down
129 // the event stream. 138 // the event stream.
130 if (!message_loop()->BelongsToCurrentThread()) { 139 if (!message_loop()->BelongsToCurrentThread()) {
131 message_loop()->PostTask( 140 message_loop()->PostTask(
132 FROM_HERE, 141 FROM_HERE,
133 NewRunnableMethod(this, &FilePathWatcherImpl::OnFilePathChanged)); 142 NewRunnableMethod(this, &FilePathWatcherImpl::OnFilePathChanged));
134 return; 143 return;
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 193
185 FSEventStreamEventId start_event = FSEventsGetCurrentEventId(); 194 FSEventStreamEventId start_event = FSEventsGetCurrentEventId();
186 195
187 base::PlatformFileInfo file_info; 196 base::PlatformFileInfo file_info;
188 if (file_util::GetFileInfo(target_, &file_info)) { 197 if (file_util::GetFileInfo(target_, &file_info)) {
189 last_modified_ = file_info.last_modified; 198 last_modified_ = file_info.last_modified;
190 first_notification_ = base::Time::Now(); 199 first_notification_ = base::Time::Now();
191 } 200 }
192 201
193 run_loop_message_loop()->PostTask(FROM_HERE, 202 run_loop_message_loop()->PostTask(FROM_HERE,
194 NewRunnableMethod(this, &FilePathWatcherImpl::UpdateEventStream, 203 NewRunnableMethod(this, &FilePathWatcherImpl::StartObserverAndEventStream,
195 start_event)); 204 start_event));
196 205
197 return true; 206 return true;
198 } 207 }
199 208
209 void FilePathWatcherImpl::StartObserverAndEventStream(
210 FSEventStreamEventId start_event) {
211 DCHECK(run_loop_message_loop()->BelongsToCurrentThread());
212 MessageLoop::current()->AddDestructionObserver(this);
213 UpdateEventStream(start_event);
214 }
215
200 void FilePathWatcherImpl::Cancel() { 216 void FilePathWatcherImpl::Cancel() {
201 if (!run_loop_message_loop().get()) { 217 if (!run_loop_message_loop().get()) {
202 // Watch was never called, so exit. 218 // Watch was never called, so exit.
219 set_cancelled();
203 return; 220 return;
204 } 221 }
205 222
206 // Switch to the CFRunLoop based thread if necessary, so we can tear down 223 // Switch to the CFRunLoop based thread if necessary, so we can tear down
207 // the event stream. 224 // the event stream.
208 if (!run_loop_message_loop()->BelongsToCurrentThread()) { 225 if (!run_loop_message_loop()->BelongsToCurrentThread()) {
209 run_loop_message_loop()->PostTask(FROM_HERE, 226 run_loop_message_loop()->PostTask(FROM_HERE,
210 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); 227 new FilePathWatcher::CancelTask(this));
211 return; 228 } else {
229 CancelOnMessageLoopThread();
212 } 230 }
231 }
213 232
214 canceled_ = true; 233 void FilePathWatcherImpl::CancelOnMessageLoopThread() {
215 if (fsevent_stream_) 234 set_cancelled();
235 if (fsevent_stream_) {
216 DestroyEventStream(); 236 DestroyEventStream();
237 MessageLoop::current()->RemoveDestructionObserver(this);
238 delegate_ = NULL;
239 }
240 }
241
242 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
243 CancelOnMessageLoopThread();
217 } 244 }
218 245
219 void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) { 246 void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) {
220 DCHECK(run_loop_message_loop()->BelongsToCurrentThread()); 247 DCHECK(run_loop_message_loop()->BelongsToCurrentThread());
221 DCHECK(MessageLoopForUI::current()); 248 DCHECK(MessageLoopForUI::current());
222 249
223 // It can happen that the watcher gets canceled while tasks that call this 250 // 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. 251 // function are still in flight, so abort if this situation is detected.
225 if (canceled_) 252 if (is_cancelled())
226 return; 253 return;
227 254
228 if (fsevent_stream_) 255 if (fsevent_stream_)
229 DestroyEventStream(); 256 DestroyEventStream();
230 257
231 base::mac::ScopedCFTypeRef<CFStringRef> cf_path(CFStringCreateWithCString( 258 base::mac::ScopedCFTypeRef<CFStringRef> cf_path(CFStringCreateWithCString(
232 NULL, target_.value().c_str(), kCFStringEncodingMacHFS)); 259 NULL, target_.value().c_str(), kCFStringEncodingMacHFS));
233 base::mac::ScopedCFTypeRef<CFStringRef> cf_dir_path(CFStringCreateWithCString( 260 base::mac::ScopedCFTypeRef<CFStringRef> cf_dir_path(CFStringCreateWithCString(
234 NULL, target_.DirName().value().c_str(), kCFStringEncodingMacHFS)); 261 NULL, target_.DirName().value().c_str(), kCFStringEncodingMacHFS));
235 CFStringRef paths_array[] = { cf_path.get(), cf_dir_path.get() }; 262 CFStringRef paths_array[] = { cf_path.get(), cf_dir_path.get() };
(...skipping 16 matching lines...) Expand all
252 FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), 279 FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(),
253 kCFRunLoopDefaultMode); 280 kCFRunLoopDefaultMode);
254 if (!FSEventStreamStart(fsevent_stream_)) { 281 if (!FSEventStreamStart(fsevent_stream_)) {
255 message_loop()->PostTask(FROM_HERE, 282 message_loop()->PostTask(FROM_HERE,
256 NewRunnableMethod(delegate_.get(), 283 NewRunnableMethod(delegate_.get(),
257 &FilePathWatcher::Delegate::OnError)); 284 &FilePathWatcher::Delegate::OnError));
258 } 285 }
259 } 286 }
260 287
261 void FilePathWatcherImpl::DestroyEventStream() { 288 void FilePathWatcherImpl::DestroyEventStream() {
262 DCHECK(run_loop_message_loop()->BelongsToCurrentThread());
263 FSEventStreamStop(fsevent_stream_); 289 FSEventStreamStop(fsevent_stream_);
264 FSEventStreamUnscheduleFromRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), 290 FSEventStreamUnscheduleFromRunLoop(fsevent_stream_, CFRunLoopGetCurrent(),
265 kCFRunLoopDefaultMode); 291 kCFRunLoopDefaultMode);
266 FSEventStreamRelease(fsevent_stream_); 292 FSEventStreamRelease(fsevent_stream_);
267 fsevent_stream_ = NULL; 293 fsevent_stream_ = NULL;
268 } 294 }
269 295
270 } // namespace 296 } // namespace
271 297
272 FilePathWatcher::FilePathWatcher() { 298 FilePathWatcher::FilePathWatcher() {
273 impl_ = new FilePathWatcherImpl(); 299 impl_ = new FilePathWatcherImpl();
274 } 300 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698