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