changeset 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 4ed384ea7f39
files src/fswatcher.cpp src/fswatcher.h
diffstat 2 files changed, 70 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/src/fswatcher.cpp	Tue Mar 13 17:58:17 2012 +0000
+++ b/src/fswatcher.cpp	Wed Mar 14 11:40:58 2012 +0000
@@ -78,8 +78,10 @@
  * whatever the fd ulimit is set to, but that's the default) and it
  * doesn't notify if a file within a directory is modified unless the
  * metadata changes. The main limitation of FSEvents is that it only
- * notifies with directory granularity, but that's OK for us so long
- * as notifications are actually provoked by file changes as well.
+ * notifies with directory granularity, but that might be OK for us so
+ * long as notifications are actually provoked by file changes as
+ * well. (In OS/X 10.7 there appear to be file-level notifications
+ * too, but that doesn't help us.)
  *
  * One other problem with FSEvents is that the API only exists on OS/X
  * 10.5 or newer -- on older versions we would have no option but to
@@ -152,6 +154,7 @@
     FsWatcher *watcher = reinterpret_cast<FsWatcher *>(clientCallBackInfo);
     const char *const *cpaths = reinterpret_cast<const char *const *>(paths);
     for (size_t i = 0; i < numEvents; ++i) {
+        std::cerr << "path " << i << " = " << cpaths[i] << std::endl;
         watcher->fsDirectoryChanged(QString::fromLocal8Bit(cpaths[i]));
     }
 }
@@ -227,11 +230,18 @@
 FsWatcher::setTrackedFilePaths(QStringList paths)
 {
 #ifdef Q_OS_MAC
-    // We don't need to do anything here, so long as addWorkDirectory
-    // has been called -- FSEvents monitors files within directories
-    // as well as the directories themselves (even though it only
-    // notifies with directory granularity)
+
+    // FSEvents will notify when any file in the directory changes,
+    // but we need to be able to check whether the file change was
+    // meaningful to us if it didn't result in any files being added
+    // or removed -- and we have to do that by examining timestamps on
+    // the files we care about
+    foreach (QString p, paths) {
+        m_trackedFileUpdates[p] = QDateTime::currentDateTime();
+    }
+
 #else
+
     QMutexLocker locker(&m_mutex);
 
     QSet<QString> alreadyWatched = 
@@ -253,6 +263,7 @@
     }
 
     debugPrint();
+
 #endif
 }
 
@@ -298,29 +309,40 @@
 void
 FsWatcher::fsDirectoryChanged(QString path)
 {
+    bool haveChanges = false;
+
     {
 	QMutexLocker locker(&m_mutex);
 
 	if (shouldIgnore(path)) return;
 
         QSet<QString> files = scanDirectory(path);
+
         if (files == m_dirContents[path]) {
+
 #ifdef DEBUG_FSWATCHER
             std::cerr << "FsWatcher: Directory " << path << " has changed, but not in a way that we are monitoring" << std::endl;
 #endif
-            return;
+
+#ifdef Q_OS_MAC
+            haveChanges = manuallyCheckTrackedFiles();
+#endif
+
         } else {
+
 #ifdef DEBUG_FSWATCHER
             std::cerr << "FsWatcher: Directory " << path << " has changed" << std::endl;
 #endif
             m_dirContents[path] = files;
+            size_t counter = ++m_lastCounter;
+            m_changes[path] = counter;
+            haveChanges = true;
         }
-
-        size_t counter = ++m_lastCounter;
-        m_changes[path] = counter;
     }
 
-    emit changed();
+    if (haveChanges) {
+        emit changed();
+    }
 }
 
 void
@@ -332,7 +354,7 @@
         // We don't check whether the file matches an ignore pattern,
         // because we are only notified for file changes if we are
         // watching the file explicitly, i.e. the file is in the
-        // tracked file paths list. So we never want to ignore them
+        // tracked file paths list. So we never want to ignore these
 
 #ifdef DEBUG_FSWATCHER
         std::cerr << "FsWatcher: Tracked file " << path << " has changed" << std::endl;
@@ -345,6 +367,38 @@
     emit changed();
 }
 
+#ifdef Q_OS_MAC
+bool
+FsWatcher::manuallyCheckTrackedFiles()
+{
+    bool foundChanges = false;
+
+    for (PathTimeMap::iterator i = m_trackedFileUpdates.begin();
+         i != m_trackedFileUpdates.end(); ++i) {
+
+        QString path = i.key();
+        QDateTime prevUpdate = i.value();
+
+        QFileInfo fi(path);
+        QDateTime currUpdate = fi.lastModified();
+
+        if (currUpdate > prevUpdate) {
+
+#ifdef DEBUG_FSWATCHER
+            std::cerr << "FsWatcher: Tracked file " << path << " has been changed since last check" << std::endl;
+#endif
+            i.value() = currUpdate;
+            
+            size_t counter = ++m_lastCounter;
+            m_changes[path] = counter;
+            foundChanges = true;
+        }
+    }
+    
+    return foundChanges;
+}
+#endif
+
 bool
 FsWatcher::shouldIgnore(QString path)
 {
@@ -383,8 +437,6 @@
             files.insert(entry);
         }
     }
-//    std::cerr << "scanDirectory:" << std::endl;
-//    foreach (QString f, files) std::cerr << f << std::endl;
     return files;
 }
 
--- a/src/fswatcher.h	Tue Mar 13 17:58:17 2012 +0000
+++ b/src/fswatcher.h	Wed Mar 14 11:40:58 2012 +0000
@@ -24,6 +24,7 @@
 #include <QSet>
 #include <QHash>
 #include <QMap>
+#include <QDateTime>
 #include <QStringList>
 
 #ifndef Q_OS_MAC
@@ -153,6 +154,9 @@
 
 #ifdef Q_OS_MAC
     void *m_stream;
+    typedef QMap<QString, QDateTime> PathTimeMap;
+    PathTimeMap m_trackedFileUpdates;
+    bool manuallyCheckTrackedFiles();
 #else
     QFileSystemWatcher m_watcher;
 #endif