changeset 538:bdc9de794839 fswatcher

Start separate FsWatcher class to manage filesystem watch policy
author Chris Cannam
date Fri, 10 Feb 2012 17:53:04 +0000
parents a4e699d32a9a
children 3935a7e621ca
files easyhg.pro src/fswatcher.cpp src/fswatcher.h
diffstat 3 files changed, 268 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/easyhg.pro	Fri Feb 10 13:08:07 2012 +0000
+++ b/easyhg.pro	Fri Feb 10 17:53:04 2012 +0000
@@ -64,7 +64,8 @@
     src/annotatedialog.h \
     src/hgignoredialog.h \
     src/versiontester.h \
-    src/squeezedlabel.h
+    src/squeezedlabel.h \
+    src/fswatcher.h
 SOURCES = \
     src/main.cpp \
     src/mainwindow.cpp \
@@ -101,7 +102,8 @@
     src/annotatedialog.cpp \
     src/hgignoredialog.cpp \
     src/versiontester.cpp \
-    src/squeezedlabel.cpp
+    src/squeezedlabel.cpp \
+    src/fswatcher.cpp
 
 macx-* {
     SOURCES += src/common_osx.mm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fswatcher.cpp	Fri Feb 10 17:53:04 2012 +0000
@@ -0,0 +1,145 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    EasyMercurial
+
+    Based on hgExplorer by Jari Korhonen
+    Copyright (c) 2010 Jari Korhonen
+    Copyright (c) 2011 Chris Cannam
+    Copyright (c) 2011 Queen Mary, University of London
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "fswatcher.h"
+
+#include <QMutexLocker>
+#include <QDir>
+
+#include <deque>
+
+FsWatcher::FsWatcher() :
+    m_lastToken(0),
+    m_lastCounter(0)
+{
+    connect(&m_watcher, SIGNAL(directoryChanged(QString)),
+	    this, SLOT(fsDirectoryChanged(QString)));
+    connect(&m_watcher, SIGNAL(fileChanged(QString)),
+	    this, SLOT(fsFileChanged(QString)));
+}
+
+FsWatcher::~FsWatcher()
+{
+}
+
+void
+FsWatcher::setWorkDirPath(QString path)
+{
+    QMutexLocker locker(&m_mutex);
+    m_watcher.removePaths(m_watcher.directories());
+    m_watcher.removePaths(m_watcher.files());
+    m_workDirPath = path;
+    addWorkDirectory(path);
+}
+
+void
+FsWatcher::addWorkDirectory(QString path)
+{
+    // QFileSystemWatcher will refuse to add a file or directory to
+    // its watch list that it is already watching -- fine -- but it
+    // prints a warning when this happens, which we wouldn't want.  So
+    // we'll check for duplicates ourselves.
+    QSet<QString> alreadyWatched = 
+	QSet<QString>::fromList(m_watcher.directories());
+    
+    std::deque<QString> pending;
+    pending.push_back(path);
+
+    while (!pending.empty()) {
+
+        QString path = pending.front();
+        pending.pop_front();
+        if (!alreadyWatched.contains(path)) {
+            m_watcher.addPath(path);
+        }
+
+        QDir d(path);
+        if (d.exists()) {
+            d.setFilter(QDir::Dirs | QDir::NoDotAndDotDot |
+                        QDir::Readable | QDir::NoSymLinks);
+            foreach (QString entry, d.entryList()) {
+                if (entry.startsWith('.')) continue;
+                QString entryPath = d.absoluteFilePath(entry);
+                pending.push_back(entryPath);
+            }
+        }
+    }
+}
+
+void
+FsWatcher::setIgnoredFilePrefixes(QStringList prefixes)
+{
+    QMutexLocker locker(&m_mutex);
+    m_ignoredPrefixes = prefixes;
+}
+
+void
+FsWatcher::setIgnoredFileSuffixes(QStringList suffixes)
+{
+    QMutexLocker locker(&m_mutex);
+    m_ignoredSuffixes = suffixes;
+}
+
+int
+FsWatcher::getNewToken()
+{
+    QMutexLocker locker(&m_mutex);
+    int token = ++m_lastToken;
+    m_tokenMap[token] = m_lastCounter;
+    return token;
+}
+
+QSet<QString>
+FsWatcher::getChangedPaths(int token)
+{
+    QMutexLocker locker(&m_mutex);
+    size_t lastUpdatedAt = m_tokenMap[token];
+    QSet<QString> changed;
+    for (QHash<QString, size_t>::const_iterator i = m_changes.begin();
+	 i != m_changes.end(); ++i) {
+	if (i.value() > lastUpdatedAt) {
+	    changed.insert(i.key());
+	}
+    }
+    m_tokenMap[token] = m_lastCounter;
+    return changed;
+}
+
+void
+FsWatcher::fsDirectoryChanged(QString path)
+{
+    {
+	QMutexLocker locker(&m_mutex);
+	if (shouldIgnore(path)) return;
+	size_t counter = ++m_lastCounter;
+	m_changes[path] = counter;
+    }
+    emit changed();
+}
+
+void
+FsWatcher::fsFileChanged(QString path)
+{
+    QMutexLocker locker(&m_mutex);
+}
+
+bool
+FsWatcher::shouldIgnore(QString path)
+{
+    
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fswatcher.h	Fri Feb 10 17:53:04 2012 +0000
@@ -0,0 +1,119 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    EasyMercurial
+
+    Based on HgExplorer by Jari Korhonen
+    Copyright (c) 2010 Jari Korhonen
+    Copyright (c) 2011 Chris Cannam
+    Copyright (c) 2011 Queen Mary, University of London
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef FSWATCHER_H
+#define FSWATCHER_H
+
+#include <QObject>
+#include <QMutex>
+#include <QString>
+#include <QSet>
+#include <QHash>
+#include <QMap>
+#include <QStringList>
+#include <QFileSystemWatcher>
+
+class FsWatcher : public QObject
+{
+    Q_OBJECT
+
+public:
+    FsWatcher();
+    virtual ~FsWatcher();
+
+    /**
+     * Set the root path of the work directory to be monitored.
+     */
+    void setWorkDirPath(QString path);
+
+    /**
+     * Provide a set of prefixes to ignore. Files whose names start
+     * with a prefix in this set will be ignored when they change.
+     */
+    void setIgnoredFilePrefixes(QStringList prefixes);
+
+    /**
+     * Provide a set of suffixes to ignore. Files whose names end
+     * with a suffix in this set will be ignored when they change.
+     */
+    void setIgnoredFileSuffixes(QStringList suffixes);
+
+    /**
+     * Return a token to identify the current caller in subsequent
+     * calls to getChangedPaths().  Only changes that occur after this
+     * has been called can be detected by the caller.
+     */
+    int getNewToken();
+    
+    /**
+     * Return a list of all non-ignored file paths that have been
+     * observed to have changed since the last call to getChangedPaths
+     * with the same token.
+     */
+    QSet<QString> getChangedPaths(int token);
+    
+signals:
+    /**
+     * Emitted when something has changed. Use the asynchronous
+     * interface to find out what.
+     */
+    void changed();
+
+private slots:
+    void fsDirectoryChanged(QString);
+    void fsFileChanged(QString);
+
+private:
+    // call with lock already held
+    void addWorkDirectory(QString path);
+
+    // call with lock already held
+    bool shouldIgnore(QString path);
+
+private:
+    /**
+     * A change associates a filename with a counter (the
+     * size_t). Each time a file is changed, its counter is assigned
+     * from m_lastCounter and m_lastCounter is incremented.
+     *
+     * This is not especially efficient -- we often want to find "all
+     * files whose counter is greater than X" which involves a
+     * traversal. Maybe something better later.
+     */
+    QHash<QString, size_t> m_changes;
+
+    /**
+     * Associates a token (the client identifier) with a counter. The
+     * counter represents the value of m_lastCounter at the moment
+     * getChangedPaths last completed for that token. Any files in
+     * m_changes whose counters are larger must have been modified.
+     */
+    QMap<int, size_t> m_tokenMap;
+
+    QStringList m_ignoredPrefixes;
+    QStringList m_ignoredSuffixes;
+
+    /// Everything in this class is synchronised.
+    QMutex m_mutex;
+
+    QString m_workDirPath;
+    int m_lastToken;
+    size_t m_lastCounter;
+    QFileSystemWatcher m_watcher;
+};
+
+#endif