Mercurial > hg > easyhg
comparison src/fswatcher.cpp @ 586:687d9e700e81 fswatcher
Check file timestamps for tracked files when a directory change is notified from FSEvents
author | Chris Cannam |
---|---|
date | Wed, 14 Mar 2012 11:40:58 +0000 |
parents | fa242885a233 |
children | 0b0444762a5d |
comparison
equal
deleted
inserted
replaced
585:fa242885a233 | 586:687d9e700e81 |
---|---|
76 * This means the Qt class uses kqueue instead on OS/X, but that | 76 * This means the Qt class uses kqueue instead on OS/X, but that |
77 * doesn't really work for us -- it can only monitor 256 files (or | 77 * doesn't really work for us -- it can only monitor 256 files (or |
78 * whatever the fd ulimit is set to, but that's the default) and it | 78 * whatever the fd ulimit is set to, but that's the default) and it |
79 * doesn't notify if a file within a directory is modified unless the | 79 * doesn't notify if a file within a directory is modified unless the |
80 * metadata changes. The main limitation of FSEvents is that it only | 80 * metadata changes. The main limitation of FSEvents is that it only |
81 * notifies with directory granularity, but that's OK for us so long | 81 * notifies with directory granularity, but that might be OK for us so |
82 * as notifications are actually provoked by file changes as well. | 82 * long as notifications are actually provoked by file changes as |
83 * well. (In OS/X 10.7 there appear to be file-level notifications | |
84 * too, but that doesn't help us.) | |
83 * | 85 * |
84 * One other problem with FSEvents is that the API only exists on OS/X | 86 * One other problem with FSEvents is that the API only exists on OS/X |
85 * 10.5 or newer -- on older versions we would have no option but to | 87 * 10.5 or newer -- on older versions we would have no option but to |
86 * use kqueue via QFileSystemWatcher. But we can't ship a binary | 88 * use kqueue via QFileSystemWatcher. But we can't ship a binary |
87 * linked with the FSEvents API to run on 10.4 without some fiddling, | 89 * linked with the FSEvents API to run on 10.4 without some fiddling, |
150 const FSEventStreamEventId eventIDs[]) | 152 const FSEventStreamEventId eventIDs[]) |
151 { | 153 { |
152 FsWatcher *watcher = reinterpret_cast<FsWatcher *>(clientCallBackInfo); | 154 FsWatcher *watcher = reinterpret_cast<FsWatcher *>(clientCallBackInfo); |
153 const char *const *cpaths = reinterpret_cast<const char *const *>(paths); | 155 const char *const *cpaths = reinterpret_cast<const char *const *>(paths); |
154 for (size_t i = 0; i < numEvents; ++i) { | 156 for (size_t i = 0; i < numEvents; ++i) { |
157 std::cerr << "path " << i << " = " << cpaths[i] << std::endl; | |
155 watcher->fsDirectoryChanged(QString::fromLocal8Bit(cpaths[i])); | 158 watcher->fsDirectoryChanged(QString::fromLocal8Bit(cpaths[i])); |
156 } | 159 } |
157 } | 160 } |
158 #endif | 161 #endif |
159 | 162 |
225 | 228 |
226 void | 229 void |
227 FsWatcher::setTrackedFilePaths(QStringList paths) | 230 FsWatcher::setTrackedFilePaths(QStringList paths) |
228 { | 231 { |
229 #ifdef Q_OS_MAC | 232 #ifdef Q_OS_MAC |
230 // We don't need to do anything here, so long as addWorkDirectory | 233 |
231 // has been called -- FSEvents monitors files within directories | 234 // FSEvents will notify when any file in the directory changes, |
232 // as well as the directories themselves (even though it only | 235 // but we need to be able to check whether the file change was |
233 // notifies with directory granularity) | 236 // meaningful to us if it didn't result in any files being added |
237 // or removed -- and we have to do that by examining timestamps on | |
238 // the files we care about | |
239 foreach (QString p, paths) { | |
240 m_trackedFileUpdates[p] = QDateTime::currentDateTime(); | |
241 } | |
242 | |
234 #else | 243 #else |
244 | |
235 QMutexLocker locker(&m_mutex); | 245 QMutexLocker locker(&m_mutex); |
236 | 246 |
237 QSet<QString> alreadyWatched = | 247 QSet<QString> alreadyWatched = |
238 QSet<QString>::fromList(m_watcher.files()); | 248 QSet<QString>::fromList(m_watcher.files()); |
239 | 249 |
251 foreach (QString path, alreadyWatched) { | 261 foreach (QString path, alreadyWatched) { |
252 m_watcher.removePath(path); | 262 m_watcher.removePath(path); |
253 } | 263 } |
254 | 264 |
255 debugPrint(); | 265 debugPrint(); |
266 | |
256 #endif | 267 #endif |
257 } | 268 } |
258 | 269 |
259 void | 270 void |
260 FsWatcher::setIgnoredFilePrefixes(QStringList prefixes) | 271 FsWatcher::setIgnoredFilePrefixes(QStringList prefixes) |
296 } | 307 } |
297 | 308 |
298 void | 309 void |
299 FsWatcher::fsDirectoryChanged(QString path) | 310 FsWatcher::fsDirectoryChanged(QString path) |
300 { | 311 { |
312 bool haveChanges = false; | |
313 | |
301 { | 314 { |
302 QMutexLocker locker(&m_mutex); | 315 QMutexLocker locker(&m_mutex); |
303 | 316 |
304 if (shouldIgnore(path)) return; | 317 if (shouldIgnore(path)) return; |
305 | 318 |
306 QSet<QString> files = scanDirectory(path); | 319 QSet<QString> files = scanDirectory(path); |
320 | |
307 if (files == m_dirContents[path]) { | 321 if (files == m_dirContents[path]) { |
322 | |
308 #ifdef DEBUG_FSWATCHER | 323 #ifdef DEBUG_FSWATCHER |
309 std::cerr << "FsWatcher: Directory " << path << " has changed, but not in a way that we are monitoring" << std::endl; | 324 std::cerr << "FsWatcher: Directory " << path << " has changed, but not in a way that we are monitoring" << std::endl; |
310 #endif | 325 #endif |
311 return; | 326 |
327 #ifdef Q_OS_MAC | |
328 haveChanges = manuallyCheckTrackedFiles(); | |
329 #endif | |
330 | |
312 } else { | 331 } else { |
332 | |
313 #ifdef DEBUG_FSWATCHER | 333 #ifdef DEBUG_FSWATCHER |
314 std::cerr << "FsWatcher: Directory " << path << " has changed" << std::endl; | 334 std::cerr << "FsWatcher: Directory " << path << " has changed" << std::endl; |
315 #endif | 335 #endif |
316 m_dirContents[path] = files; | 336 m_dirContents[path] = files; |
317 } | 337 size_t counter = ++m_lastCounter; |
318 | 338 m_changes[path] = counter; |
319 size_t counter = ++m_lastCounter; | 339 haveChanges = true; |
320 m_changes[path] = counter; | 340 } |
321 } | 341 } |
322 | 342 |
323 emit changed(); | 343 if (haveChanges) { |
344 emit changed(); | |
345 } | |
324 } | 346 } |
325 | 347 |
326 void | 348 void |
327 FsWatcher::fsFileChanged(QString path) | 349 FsWatcher::fsFileChanged(QString path) |
328 { | 350 { |
330 QMutexLocker locker(&m_mutex); | 352 QMutexLocker locker(&m_mutex); |
331 | 353 |
332 // We don't check whether the file matches an ignore pattern, | 354 // We don't check whether the file matches an ignore pattern, |
333 // because we are only notified for file changes if we are | 355 // because we are only notified for file changes if we are |
334 // watching the file explicitly, i.e. the file is in the | 356 // watching the file explicitly, i.e. the file is in the |
335 // tracked file paths list. So we never want to ignore them | 357 // tracked file paths list. So we never want to ignore these |
336 | 358 |
337 #ifdef DEBUG_FSWATCHER | 359 #ifdef DEBUG_FSWATCHER |
338 std::cerr << "FsWatcher: Tracked file " << path << " has changed" << std::endl; | 360 std::cerr << "FsWatcher: Tracked file " << path << " has changed" << std::endl; |
339 #endif | 361 #endif |
340 | 362 |
342 m_changes[path] = counter; | 364 m_changes[path] = counter; |
343 } | 365 } |
344 | 366 |
345 emit changed(); | 367 emit changed(); |
346 } | 368 } |
369 | |
370 #ifdef Q_OS_MAC | |
371 bool | |
372 FsWatcher::manuallyCheckTrackedFiles() | |
373 { | |
374 bool foundChanges = false; | |
375 | |
376 for (PathTimeMap::iterator i = m_trackedFileUpdates.begin(); | |
377 i != m_trackedFileUpdates.end(); ++i) { | |
378 | |
379 QString path = i.key(); | |
380 QDateTime prevUpdate = i.value(); | |
381 | |
382 QFileInfo fi(path); | |
383 QDateTime currUpdate = fi.lastModified(); | |
384 | |
385 if (currUpdate > prevUpdate) { | |
386 | |
387 #ifdef DEBUG_FSWATCHER | |
388 std::cerr << "FsWatcher: Tracked file " << path << " has been changed since last check" << std::endl; | |
389 #endif | |
390 i.value() = currUpdate; | |
391 | |
392 size_t counter = ++m_lastCounter; | |
393 m_changes[path] = counter; | |
394 foundChanges = true; | |
395 } | |
396 } | |
397 | |
398 return foundChanges; | |
399 } | |
400 #endif | |
347 | 401 |
348 bool | 402 bool |
349 FsWatcher::shouldIgnore(QString path) | 403 FsWatcher::shouldIgnore(QString path) |
350 { | 404 { |
351 QFileInfo fi(path); | 405 QFileInfo fi(path); |
381 if (entry.startsWith('.')) continue; | 435 if (entry.startsWith('.')) continue; |
382 if (shouldIgnore(entry)) continue; | 436 if (shouldIgnore(entry)) continue; |
383 files.insert(entry); | 437 files.insert(entry); |
384 } | 438 } |
385 } | 439 } |
386 // std::cerr << "scanDirectory:" << std::endl; | |
387 // foreach (QString f, files) std::cerr << f << std::endl; | |
388 return files; | 440 return files; |
389 } | 441 } |
390 | 442 |
391 void | 443 void |
392 FsWatcher::debugPrint() | 444 FsWatcher::debugPrint() |