Chromium Code Reviews| 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 |