OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |