diff options
author | Erik Verbruggen <erik.verbruggen@me.com> | 2013-11-29 12:55:59 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-02-14 10:51:50 +0100 |
commit | 4273c14e57d26cf2dde90ed9db1f463a25b327bd (patch) | |
tree | 9a23f10aa5b44b25418ddaa221b18558fa12a5db /src/corelib | |
parent | 97c187da3c1381bc55dd16976bf9fb3c773d0047 (diff) |
Mac: FSEvents-based QFileSystemWatcherEngine.
Use FSEvents to monitor changes in the filesystem instead of the kqueue
implementation. This removes the limit of wathed files: kqueue uses a
file descriptor for each file monitored, for which the ulimit was set by
default to 256. Now the OSX implementation on par with the other major
desktop platforms.
Change-Id: I2d46cca811978621989fd35201138df88a37c0fb
Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com>
Diffstat (limited to 'src/corelib')
-rw-r--r-- | src/corelib/io/io.pri | 4 | ||||
-rw-r--r-- | src/corelib/io/qfilesystemwatcher.cpp | 8 | ||||
-rw-r--r-- | src/corelib/io/qfilesystemwatcher_fsevents.mm | 507 | ||||
-rw-r--r-- | src/corelib/io/qfilesystemwatcher_fsevents_p.h | 142 |
4 files changed, 659 insertions, 2 deletions
diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index 3f2f025aeb..989e4644c7 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -135,6 +135,10 @@ win32 { OBJECTIVE_SOURCES += io/qurl_mac.mm } mac { + osx { + OBJECTIVE_SOURCES += io/qfilesystemwatcher_fsevents.mm + HEADERS += io/qfilesystemwatcher_fsevents_p.h + } macx { SOURCES += io/qstandardpaths_mac.cpp } else:ios { diff --git a/src/corelib/io/qfilesystemwatcher.cpp b/src/corelib/io/qfilesystemwatcher.cpp index d1deae2d7b..8cc6ad0552 100644 --- a/src/corelib/io/qfilesystemwatcher.cpp +++ b/src/corelib/io/qfilesystemwatcher.cpp @@ -60,8 +60,10 @@ # include "qfilesystemwatcher_win_p.h" #elif defined(USE_INOTIFY) # include "qfilesystemwatcher_inotify_p.h" -#elif defined(Q_OS_FREEBSD) || defined(Q_OS_MAC) +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_IOS) || (defined(Q_OS_OSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7) # include "qfilesystemwatcher_kqueue_p.h" +#elif defined(Q_OS_OSX) && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_6 +# include "qfilesystemwatcher_fsevents_p.h" #endif QT_BEGIN_NAMESPACE @@ -74,8 +76,10 @@ QFileSystemWatcherEngine *QFileSystemWatcherPrivate::createNativeEngine(QObject // there is a chance that inotify may fail on Linux pre-2.6.13 (August // 2005), so we can't just new inotify directly. return QInotifyFileSystemWatcherEngine::create(parent); -#elif defined(Q_OS_FREEBSD) || defined(Q_OS_MAC) +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_IOS) || (defined(Q_OS_OSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7) return QKqueueFileSystemWatcherEngine::create(parent); +#elif defined(Q_OS_OSX) && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_6 + return QFseventsFileSystemWatcherEngine::create(parent); #else Q_UNUSED(parent); return 0; diff --git a/src/corelib/io/qfilesystemwatcher_fsevents.mm b/src/corelib/io/qfilesystemwatcher_fsevents.mm new file mode 100644 index 0000000000..cb6ddd913e --- /dev/null +++ b/src/corelib/io/qfilesystemwatcher_fsevents.mm @@ -0,0 +1,507 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qplatformdefs.h> + +#include "qdiriterator.h" +#include "qfilesystemwatcher.h" +#include "qfilesystemwatcher_fsevents_p.h" +#include "private/qcore_unix_p.h" +#include "kernel/qcore_mac_p.h" + +#ifndef QT_NO_FILESYSTEMWATCHER + +#include <qdebug.h> +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qvarlengtharray.h> + +//#define FSEVENT_DEBUG +#ifdef FSEVENT_DEBUG +# define DEBUG if (true) qDebug +#else +# define DEBUG if (false) qDebug +#endif + +QT_BEGIN_NAMESPACE + +static void callBackFunction(ConstFSEventStreamRef streamRef, + void *clientCallBackInfo, + size_t numEvents, + void *eventPaths, + const FSEventStreamEventFlags eventFlags[], + const FSEventStreamEventId eventIds[]) +{ + char **paths = static_cast<char **>(eventPaths); + QFseventsFileSystemWatcherEngine *engine = static_cast<QFseventsFileSystemWatcherEngine *>(clientCallBackInfo); + engine->processEvent(streamRef, numEvents, paths, eventFlags, eventIds); +} + +void QFseventsFileSystemWatcherEngine::checkDir(DirsByName::iterator &it) +{ + QT_STATBUF st; + const QString &name = it.key(); + Info &info = it->dirInfo; + const int res = QT_STAT(QFile::encodeName(name), &st); + if (res == -1) { + derefPath(info.watchedPath); + emit emitDirectoryChanged(info.origPath, true); + it = watchedDirectories.erase(it); + } else if (st.st_ctimespec != info.ctime || st.st_mode != info.mode) { + info.ctime = st.st_ctimespec; + info.mode = st.st_mode; + emit emitDirectoryChanged(info.origPath, false); + ++it; + } else { + bool dirChanged = false; + InfoByName &entries = it->entries; + // check known entries: + for (InfoByName::iterator i = entries.begin(); i != entries.end(); ) { + if (QT_STAT(QFile::encodeName(i.key()), &st) == -1) { + // entry disappeared + dirChanged = true; + i = entries.erase(i); + } else { + if (i->ctime != st.st_ctimespec || i->mode != st.st_mode) { + // entry changed + dirChanged = true; + i->ctime = st.st_ctimespec; + i->mode = st.st_mode; + } + ++i; + } + } + // check for new entries: + QDirIterator dirIt(name); + while (dirIt.hasNext()) { + dirIt.next(); + QString entryName = dirIt.filePath(); + if (!entries.contains(entryName)) { + dirChanged = true; + QT_STATBUF st; + if (QT_STAT(QFile::encodeName(entryName), &st) == -1) + continue; + entries.insert(entryName, Info(QString(), st.st_ctimespec, st.st_mode, QString())); + + } + } + if (dirChanged) + emit emitDirectoryChanged(info.origPath, false); + } +} + +void QFseventsFileSystemWatcherEngine::rescanDirs(const QString &path) +{ + for (DirsByName::iterator it = watchedDirectories.begin(); it != watchedDirectories.end(); ) { + if (it.key().startsWith(path)) + checkDir(it); + else + ++it; + } +} + +void QFseventsFileSystemWatcherEngine::rescanFiles(InfoByName &filesInPath) +{ + for (InfoByName::iterator it = filesInPath.begin(); it != filesInPath.end(); ) { + QT_STATBUF st; + QString name = it.key(); + const int res = QT_STAT(QFile::encodeName(name), &st); + if (res == -1) { + derefPath(it->watchedPath); + emit emitFileChanged(it.value().origPath, true); + it = filesInPath.erase(it); + continue; + } else if (st.st_ctimespec != it->ctime || st.st_mode != it->mode) { + it->ctime = st.st_ctimespec; + it->mode = st.st_mode; + emit emitFileChanged(it.value().origPath, false); + } + + ++it; + } +} + +void QFseventsFileSystemWatcherEngine::rescanFiles(const QString &path) +{ + for (FilesByPath::iterator i = watchedFiles.begin(); i != watchedFiles.end(); ) { + if (i.key().startsWith(path)) { + rescanFiles(i.value()); + if (i.value().isEmpty()) { + i = watchedFiles.erase(i); + continue; + } + } + + ++i; + } +} + +void QFseventsFileSystemWatcherEngine::processEvent(ConstFSEventStreamRef streamRef, + size_t numEvents, + char **eventPaths, + const FSEventStreamEventFlags eventFlags[], + const FSEventStreamEventId eventIds[]) +{ +#if defined(Q_OS_OSX) && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_6 + Q_UNUSED(streamRef); + + QMutexLocker locker(&lock); + + for (size_t i = 0; i < numEvents; ++i) { + FSEventStreamEventFlags eFlags = eventFlags[i]; + DEBUG("Change %llu in %s, flags %x", eventIds[i], eventPaths[i], (unsigned int)eFlags); + QString path = QFile::decodeName(eventPaths[i]); + if (path.endsWith(QDir::separator())) + path = path.mid(0, path.size() - 1); + + if (eFlags & kFSEventStreamEventFlagMustScanSubDirs) { + DEBUG("\tmust rescan directory because of coalesced events"); + if (eFlags & kFSEventStreamEventFlagUserDropped) + DEBUG("\t\t... user dropped."); + if (eFlags & kFSEventStreamEventFlagKernelDropped) + DEBUG("\t\t... kernel dropped."); + rescanDirs(path); + rescanFiles(path); + continue; + } + + if (eFlags & kFSEventStreamEventFlagEventIdsWrapped) { + DEBUG("\tthe event ids wrapped"); + // TODO: verify if we need to do something + } + + if (eFlags & kFSEventStreamEventFlagRootChanged) { + // re-check everything: + DirsByName::iterator dirIt = watchedDirectories.find(path); + if (dirIt != watchedDirectories.end()) + checkDir(dirIt); + rescanFiles(path); + continue; + } + + if ((eFlags & kFSEventStreamEventFlagItemIsDir) && (eFlags & kFSEventStreamEventFlagItemRemoved)) + rescanDirs(path); + + // check watched directories: + DirsByName::iterator dirIt = watchedDirectories.find(path); + if (dirIt != watchedDirectories.end()) + checkDir(dirIt); + + // check watched files: + FilesByPath::iterator pIt = watchedFiles.find(path); + if (pIt != watchedFiles.end()) + rescanFiles(pIt.value()); + } +#else + // This is a work-around for moc: when we put the version check at the top of the header file, + // moc will still see the Q_OBJECT macro and generate a meta-object when compiling for 10.6, + // which obviously won't link. + // + // So the trick is to still compile this class on 10.6, but never instantiate it. + + Q_UNUSED(streamRef); + Q_UNUSED(numEvents); + Q_UNUSED(eventPaths); + Q_UNUSED(eventFlags); + Q_UNUSED(eventIds); +#endif +} + +void QFseventsFileSystemWatcherEngine::doEmitFileChanged(const QString path, bool removed) +{ + emit fileChanged(path, removed); +} + +void QFseventsFileSystemWatcherEngine::doEmitDirectoryChanged(const QString path, bool removed) +{ + emit directoryChanged(path, removed); +} + +QFseventsFileSystemWatcherEngine *QFseventsFileSystemWatcherEngine::create(QObject *parent) +{ + return new QFseventsFileSystemWatcherEngine(parent); +} + +QFseventsFileSystemWatcherEngine::QFseventsFileSystemWatcherEngine(QObject *parent) + : QFileSystemWatcherEngine(parent) + , stream(0) +{ + + // We cannot use signal-to-signal queued connections, because the + // QSignalSpy cannot spot signals fired from other/alien threads. + connect(this, SIGNAL(emitDirectoryChanged(const QString, bool)), + this, SLOT(doEmitDirectoryChanged(const QString, bool)), Qt::QueuedConnection); + connect(this, SIGNAL(emitFileChanged(const QString, bool)), + this, SLOT(doEmitFileChanged(const QString, bool)), Qt::QueuedConnection); + + queue = dispatch_queue_create("org.qt-project.QFseventsFileSystemWatcherEngine", NULL); +} + +QFseventsFileSystemWatcherEngine::~QFseventsFileSystemWatcherEngine() +{ + if (stream) + FSEventStreamStop(stream); + + // The assumption with the locking strategy is that this class cannot and will not be subclassed! + QMutexLocker locker(&lock); + + stopStream(); + dispatch_release(queue); +} + +QStringList QFseventsFileSystemWatcherEngine::addPaths(const QStringList &paths, + QStringList *files, + QStringList *directories) +{ + if (stream) + FSEventStreamFlushSync(stream); + + QMutexLocker locker(&lock); + + bool newWatchPathsFound = false; + + QStringList p = paths; + QMutableListIterator<QString> it(p); + while (it.hasNext()) { + QString origPath = it.next(); + QString realPath = origPath; + if (realPath.endsWith(QDir::separator())) + realPath = realPath.mid(0, realPath.size() - 1); + QString watchedPath, parentPath; + + realPath = QFileInfo(realPath).canonicalFilePath(); + QFileInfo fi(realPath); + if (realPath.isEmpty()) + continue; + + QT_STATBUF st; + if (QT_STAT(QFile::encodeName(realPath), &st) == -1) + continue; + + const bool isDir = S_ISDIR(st.st_mode); + if (isDir) { + if (watchedDirectories.contains(realPath)) + continue; + directories->append(origPath); + watchedPath = realPath; + it.remove(); + } else { + if (files->contains(origPath)) + continue; + files->append(origPath); + it.remove(); + + watchedPath = fi.path(); + parentPath = watchedPath; + } + + for (PathRefCounts::const_iterator i = watchedPaths.begin(), ei = watchedPaths.end(); i != ei; ++i) { + if (watchedPath.startsWith(i.key())) { + watchedPath = i.key(); + break; + } + } + + PathRefCounts::iterator it = watchedPaths.find(watchedPath); + if (it == watchedPaths.end()) { + newWatchPathsFound = true; + watchedPaths.insert(watchedPath, 1); + } else { + ++it.value(); + } + + Info info(origPath, st.st_ctimespec, st.st_mode, watchedPath); + if (isDir) { + DirInfo dirInfo; + dirInfo.dirInfo = info; + dirInfo.entries = scanForDirEntries(realPath); + watchedDirectories.insert(realPath, dirInfo); + } else { + watchedFiles[parentPath].insert(realPath, info); + } + } + + if (newWatchPathsFound) { + stopStream(); + if (!startStream()) + p = paths; + } + + return p; +} + +QStringList QFseventsFileSystemWatcherEngine::removePaths(const QStringList &paths, + QStringList *files, + QStringList *directories) +{ + QMutexLocker locker(&lock); + + QStringList p = paths; + QMutableListIterator<QString> it(p); + while (it.hasNext()) { + QString origPath = it.next(); + QString realPath = origPath; + if (realPath.endsWith(QDir::separator())) + realPath = realPath.mid(0, realPath.size() - 1); + + QFileInfo fi(realPath); + realPath = fi.canonicalFilePath(); + + if (fi.isDir()) { + DirsByName::iterator dirIt = watchedDirectories.find(realPath); + if (dirIt != watchedDirectories.end()) { + derefPath(dirIt->dirInfo.watchedPath); + watchedDirectories.erase(dirIt); + directories->removeAll(origPath); + it.remove(); + } + } else { + QFileInfo fi(realPath); + QString parentPath = fi.path(); + FilesByPath::iterator pIt = watchedFiles.find(parentPath); + if (pIt != watchedFiles.end()) { + InfoByName &filesInDir = pIt.value(); + InfoByName::iterator fIt = filesInDir.find(realPath); + if (fIt != filesInDir.end()) { + derefPath(fIt->watchedPath); + filesInDir.erase(fIt); + if (filesInDir.isEmpty()) + watchedFiles.erase(pIt); + files->removeAll(origPath); + it.remove(); + } + } + } + } + + return p; +} + +bool QFseventsFileSystemWatcherEngine::startStream() +{ + if (watchedPaths.isEmpty()) + return false; + + DEBUG() << "Starting stream with paths" << watchedPaths.keys(); + + NSMutableArray *pathsToWatch = [NSMutableArray arrayWithCapacity:watchedPaths.size()]; + for (PathRefCounts::const_iterator i = watchedPaths.begin(), ei = watchedPaths.end(); i != ei; ++i) + [pathsToWatch addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(i.key()))]; + + struct FSEventStreamContext callBackInfo = { + 0, + this, + NULL, + NULL, + NULL + }; + const CFAbsoluteTime latency = .5; // in seconds + FSEventStreamCreateFlags flags = kFSEventStreamCreateFlagWatchRoot; + + stream = FSEventStreamCreate(NULL, + &callBackFunction, + &callBackInfo, + reinterpret_cast<CFArrayRef>(pathsToWatch), + kFSEventStreamEventIdSinceNow, + latency, + flags); + + if (!stream) { + DEBUG() << "Failed to create stream!"; + return false; + } + + FSEventStreamSetDispatchQueue(stream, queue); + + if (FSEventStreamStart(stream)) { + DEBUG() << "Stream started successfully."; + return true; + } else { + DEBUG() << "Stream failed to start!"; + return false; + } +} + +void QFseventsFileSystemWatcherEngine::stopStream(bool isStopped) +{ + if (stream) { + if (!isStopped) + FSEventStreamStop(stream); + FSEventStreamInvalidate(stream); + FSEventStreamRelease(stream); + stream = 0; + DEBUG() << "Stream stopped."; + } +} + +QFseventsFileSystemWatcherEngine::InfoByName QFseventsFileSystemWatcherEngine::scanForDirEntries(const QString &path) +{ + InfoByName entries; + + QDirIterator it(path); + while (it.hasNext()) { + it.next(); + QString entryName = it.filePath(); + QT_STATBUF st; + if (QT_STAT(QFile::encodeName(entryName), &st) == -1) + continue; + entries.insert(entryName, Info(QString(), st.st_ctimespec, st.st_mode, QString())); + } + + return entries; +} + +void QFseventsFileSystemWatcherEngine::derefPath(const QString &watchedPath) +{ + PathRefCounts::iterator it = watchedPaths.find(watchedPath); + if (it == watchedPaths.end()) + return; + if (--it.value() < 1) { + watchedPaths.erase(it); + stopStream(); + startStream(); + } +} + +#endif //QT_NO_FILESYSTEMWATCHER + +QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystemwatcher_fsevents_p.h b/src/corelib/io/qfilesystemwatcher_fsevents_p.h new file mode 100644 index 0000000000..c899c556c8 --- /dev/null +++ b/src/corelib/io/qfilesystemwatcher_fsevents_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESYSTEMWATCHER_FSEVENTS_P_H +#define QFILESYSTEMWATCHER_FSEVENTS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qfilesystemwatcher_p.h" + +#include <QtCore/qmutex.h> +#include <QtCore/qhash.h> +#include <QtCore/qthread.h> +#include <QtCore/qvector.h> +#include <QtCore/qsocketnotifier.h> + +#include <dispatch/dispatch.h> +#include <CoreServices/CoreServices.h> + +#ifndef QT_NO_FILESYSTEMWATCHER + +QT_BEGIN_NAMESPACE + +class QFseventsFileSystemWatcherEngine : public QFileSystemWatcherEngine +{ + Q_OBJECT +public: + ~QFseventsFileSystemWatcherEngine(); + + static QFseventsFileSystemWatcherEngine *create(QObject *parent); + + QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories); + QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories); + + void processEvent(ConstFSEventStreamRef streamRef, size_t numEvents, char **eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]); + +Q_SIGNALS: + void emitFileChanged(const QString path, bool removed); + void emitDirectoryChanged(const QString path, bool removed); + +private slots: + void doEmitFileChanged(const QString path, bool removed); + void doEmitDirectoryChanged(const QString path, bool removed); + +private: + struct Info { + QString origPath; + timespec ctime; + mode_t mode; + QString watchedPath; + + Info(): mode(0) + { + ctime.tv_sec = 0; + ctime.tv_nsec = 0; + } + + Info(const QString &origPath, const timespec &ctime, mode_t mode, const QString &watchedPath) + : origPath(origPath) + , ctime(ctime) + , mode(mode) + , watchedPath(watchedPath) + {} + }; + typedef QHash<QString, Info> InfoByName; + typedef QHash<QString, InfoByName> FilesByPath; + struct DirInfo { + Info dirInfo; + InfoByName entries; + }; + typedef QHash<QString, DirInfo> DirsByName; + typedef QHash<QString, qint64> PathRefCounts; + + QFseventsFileSystemWatcherEngine(QObject *parent); + bool startStream(); + void stopStream(bool isStopped = false); + InfoByName scanForDirEntries(const QString &path); + void derefPath(const QString &watchedPath); + void checkDir(DirsByName::iterator &it); + void rescanDirs(const QString &path); + void rescanFiles(InfoByName &filesInPath); + void rescanFiles(const QString &path); + + QMutex lock; + dispatch_queue_t queue; + FSEventStreamRef stream; + FilesByPath watchedFiles; + DirsByName watchedDirectories; + PathRefCounts watchedPaths; +}; + +QT_END_NAMESPACE + +#endif //QT_NO_FILESYSTEMWATCHER +#endif // QFILESYSTEMWATCHER_FSEVENTS_P_H |