diff options
Diffstat (limited to 'src/corelib/io/qfilesystemwatcher_fsevents.cpp')
-rw-r--r-- | src/corelib/io/qfilesystemwatcher_fsevents.cpp | 492 |
1 files changed, 0 insertions, 492 deletions
diff --git a/src/corelib/io/qfilesystemwatcher_fsevents.cpp b/src/corelib/io/qfilesystemwatcher_fsevents.cpp deleted file mode 100644 index 95da897974..0000000000 --- a/src/corelib/io/qfilesystemwatcher_fsevents.cpp +++ /dev/null @@ -1,492 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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, Nokia gives you certain additional -** rights. These rights are described in the Nokia 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#define _DARWIN_USE_64_BIT_INODE -#include <qplatformdefs.h> - -#include "qfilesystemwatcher.h" -#include "qfilesystemwatcher_fsevents_p.h" - -#ifndef QT_NO_FILESYSTEMWATCHER - -#include <qdebug.h> -#include <qfile.h> -#include <qdatetime.h> -#include <qfileinfo.h> -#include <qvarlengtharray.h> - -#include <mach/mach.h> -#include <sys/types.h> -#include <CoreFoundation/CFRunLoop.h> -#include <CoreFoundation/CFUUID.h> -#include <CoreServices/CoreServices.h> -#include <AvailabilityMacros.h> -#include <private/qcore_mac_p.h> - -QT_BEGIN_NAMESPACE - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 -// Static operator overloading so for the sake of some convieniece. -// They only live in this compilation unit to avoid polluting Qt in general. -static bool operator==(const struct ::timespec &left, const struct ::timespec &right) -{ - return left.tv_sec == right.tv_sec - && left.tv_nsec == right.tv_nsec; -} - -static bool operator==(const struct ::stat &left, const struct ::stat &right) -{ - return left.st_dev == right.st_dev - && left.st_mode == right.st_mode - && left.st_size == right.st_size - && left.st_ino == right.st_ino - && left.st_uid == right.st_uid - && left.st_gid == right.st_gid - && left.st_mtimespec == right.st_mtimespec - && left.st_ctimespec == right.st_ctimespec - && left.st_flags == right.st_flags; -} - -static bool operator!=(const struct ::stat &left, const struct ::stat &right) -{ - return !(operator==(left, right)); -} - - -static void addPathToHash(PathHash &pathHash, const QString &key, const QFileInfo &fileInfo, - const QString &path) -{ - PathInfoList &list = pathHash[key]; - list.push_back(PathInfo(path, - fileInfo.canonicalFilePath().normalized(QString::NormalizationForm_D).toUtf8())); - pathHash.insert(key, list); -} - -static void removePathFromHash(PathHash &pathHash, const QString &key, const QString &path) -{ - PathInfoList &list = pathHash[key]; - // We make the assumption that the list contains unique paths - PathInfoList::iterator End = list.end(); - PathInfoList::iterator it = list.begin(); - while (it != End) { - if (it->originalPath == path) { - list.erase(it); - break; - } - ++it; - } - if (list.isEmpty()) - pathHash.remove(key); -} - -static void stopFSStream(FSEventStreamRef stream) -{ - if (stream) { - FSEventStreamStop(stream); - FSEventStreamInvalidate(stream); - } -} - -static QString createFSStreamPath(const QString &absolutePath) -{ - // The path returned has a trailing slash, so ensure that here. - QString string = absolutePath; - string.reserve(string.size() + 1); - string.append(QLatin1Char('/')); - return string; -} - -static void cleanupFSStream(FSEventStreamRef stream) -{ - if (stream) - FSEventStreamRelease(stream); -} - -const FSEventStreamCreateFlags QtFSEventFlags = (kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagNoDefer /* | kFSEventStreamCreateFlagWatchRoot*/); - -const CFTimeInterval Latency = 0.033; // This will do updates 30 times a second which is probably more than you need. -#endif - -QFSEventsFileSystemWatcherEngine::QFSEventsFileSystemWatcherEngine() - : fsStream(0), pathsToWatch(0), threadsRunLoop(0) -{ -} - -QFSEventsFileSystemWatcherEngine::~QFSEventsFileSystemWatcherEngine() -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - // I assume that at this point, QFileSystemWatcher has already called stop - // on me, so I don't need to invalidate or stop my stream, simply - // release it. - cleanupFSStream(fsStream); - if (pathsToWatch) - CFRelease(pathsToWatch); -#endif -} - -QFSEventsFileSystemWatcherEngine *QFSEventsFileSystemWatcherEngine::create() -{ - return new QFSEventsFileSystemWatcherEngine(); -} - -QStringList QFSEventsFileSystemWatcherEngine::addPaths(const QStringList &paths, - QStringList *files, - QStringList *directories) -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - stop(); - wait(); - QMutexLocker locker(&mutex); - QStringList failedToAdd; - // if we have a running FSStreamEvent, we have to kill it, we'll re-add the stream soon. - FSEventStreamEventId idToCheck; - if (fsStream) { - idToCheck = FSEventStreamGetLatestEventId(fsStream); - cleanupFSStream(fsStream); - } else { - idToCheck = kFSEventStreamEventIdSinceNow; - } - - // Brain-dead approach, but works. FSEvents actually can already read sub-trees, but since it's - // work to figure out if we are doing a double register, we just register it twice as FSEvents - // seems smart enough to only deliver one event. We also duplicate directory entries in here - // (e.g., if you watch five files in the same directory, you get that directory included in the - // array 5 times). This stupidity also makes remove work correctly though. I'll freely admit - // that we could make this a bit smarter. If you do, check the auto-tests, they should catch at - // least a couple of the issues. - QCFType<CFMutableArrayRef> tmpArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - for (int i = 0; i < paths.size(); ++i) { - const QString &path = paths.at(i); - - QFileInfo fileInfo(path); - if (!fileInfo.exists()) { - failedToAdd.append(path); - continue; - } - - if (fileInfo.isDir()) { - if (directories->contains(path)) { - failedToAdd.append(path); - continue; - } else { - directories->append(path); - // Full file path for dirs. - QCFString cfpath(createFSStreamPath(fileInfo.canonicalFilePath())); - addPathToHash(dirPathInfoHash, cfpath, fileInfo, path); - CFArrayAppendValue(tmpArray, cfpath); - } - } else { - if (files->contains(path)) { - failedToAdd.append(path); - continue; - } else { - // Just the absolute path (minus it's filename) for files. - QCFString cfpath(createFSStreamPath(fileInfo.canonicalPath())); - files->append(path); - addPathToHash(filePathInfoHash, cfpath, fileInfo, path); - CFArrayAppendValue(tmpArray, cfpath); - } - } - } - - if (!pathsToWatch && failedToAdd.size() == paths.size()) { - return failedToAdd; - } - - if (CFArrayGetCount(tmpArray) > 0) { - if (pathsToWatch) { - CFArrayAppendArray(tmpArray, pathsToWatch, CFRangeMake(0, CFArrayGetCount(pathsToWatch))); - CFRelease(pathsToWatch); - } - pathsToWatch = CFArrayCreateCopy(kCFAllocatorDefault, tmpArray); - } - - FSEventStreamContext context = { 0, this, 0, 0, 0 }; - fsStream = FSEventStreamCreate(kCFAllocatorDefault, - QFSEventsFileSystemWatcherEngine::fseventsCallback, - &context, pathsToWatch, - idToCheck, Latency, QtFSEventFlags); - warmUpFSEvents(); - - return failedToAdd; -#else - Q_UNUSED(paths); - Q_UNUSED(files); - Q_UNUSED(directories); - return QStringList(); -#endif -} - -void QFSEventsFileSystemWatcherEngine::warmUpFSEvents() -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - // This function assumes that the mutex has already been grabbed before calling it. - // It exits with the mutex still locked (Q_ASSERT(mutex.isLocked()) ;-). - start(); - waitCondition.wait(&mutex); -#endif -} - -QStringList QFSEventsFileSystemWatcherEngine::removePaths(const QStringList &paths, - QStringList *files, - QStringList *directories) -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - stop(); - wait(); - QMutexLocker locker(&mutex); - // short circuit for smarties that call remove before add and we have nothing. - if (pathsToWatch == 0) - return paths; - QStringList failedToRemove; - // if we have a running FSStreamEvent, we have to stop it, we'll re-add the stream soon. - FSEventStreamEventId idToCheck; - if (fsStream) { - idToCheck = FSEventStreamGetLatestEventId(fsStream); - cleanupFSStream(fsStream); - fsStream = 0; - } else { - idToCheck = kFSEventStreamEventIdSinceNow; - } - - CFIndex itemCount = CFArrayGetCount(pathsToWatch); - QCFType<CFMutableArrayRef> tmpArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, itemCount, - pathsToWatch); - CFRelease(pathsToWatch); - pathsToWatch = 0; - for (int i = 0; i < paths.size(); ++i) { - // Get the itemCount at the beginning to avoid any overruns during the iteration. - itemCount = CFArrayGetCount(tmpArray); - const QString &path = paths.at(i); - QFileInfo fi(path); - QCFString cfpath(createFSStreamPath(fi.canonicalPath())); - - CFIndex index = CFArrayGetFirstIndexOfValue(tmpArray, CFRangeMake(0, itemCount), cfpath); - if (index != -1) { - CFArrayRemoveValueAtIndex(tmpArray, index); - files->removeAll(path); - removePathFromHash(filePathInfoHash, cfpath, path); - } else { - // Could be a directory we are watching instead. - QCFString cfdirpath(createFSStreamPath(fi.canonicalFilePath())); - index = CFArrayGetFirstIndexOfValue(tmpArray, CFRangeMake(0, itemCount), cfdirpath); - if (index != -1) { - CFArrayRemoveValueAtIndex(tmpArray, index); - directories->removeAll(path); - removePathFromHash(dirPathInfoHash, cfpath, path); - } else { - failedToRemove.append(path); - } - } - } - itemCount = CFArrayGetCount(tmpArray); - if (itemCount != 0) { - pathsToWatch = CFArrayCreateCopy(kCFAllocatorDefault, tmpArray); - - FSEventStreamContext context = { 0, this, 0, 0, 0 }; - fsStream = FSEventStreamCreate(kCFAllocatorDefault, - QFSEventsFileSystemWatcherEngine::fseventsCallback, - &context, pathsToWatch, idToCheck, Latency, QtFSEventFlags); - warmUpFSEvents(); - } - return failedToRemove; -#else - Q_UNUSED(paths); - Q_UNUSED(files); - Q_UNUSED(directories); - return QStringList(); -#endif -} - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 -void QFSEventsFileSystemWatcherEngine::updateList(PathInfoList &list, bool directory, bool emitSignals) -{ - PathInfoList::iterator End = list.end(); - PathInfoList::iterator it = list.begin(); - while (it != End) { - struct ::stat newInfo; - if (::stat(it->absolutePath, &newInfo) == 0) { - if (emitSignals) { - if (newInfo != it->savedInfo) { - it->savedInfo = newInfo; - if (directory) - emit directoryChanged(it->originalPath, false); - else - emit fileChanged(it->originalPath, false); - } - } else { - it->savedInfo = newInfo; - } - } else { - if (errno == ENOENT) { - if (emitSignals) { - if (directory) - emit directoryChanged(it->originalPath, true); - else - emit fileChanged(it->originalPath, true); - } - it = list.erase(it); - continue; - } else { - qWarning("%s:%d:QFSEventsFileSystemWatcherEngine: stat error on %s:%s", - __FILE__, __LINE__, qPrintable(it->originalPath), strerror(errno)); - - } - } - ++it; - } -} - -void QFSEventsFileSystemWatcherEngine::updateHash(PathHash &pathHash) -{ - PathHash::iterator HashEnd = pathHash.end(); - PathHash::iterator it = pathHash.begin(); - const bool IsDirectory = (&pathHash == &dirPathInfoHash); - while (it != HashEnd) { - updateList(it.value(), IsDirectory, false); - if (it.value().isEmpty()) - it = pathHash.erase(it); - else - ++it; - } -} -#endif - -void QFSEventsFileSystemWatcherEngine::fseventsCallback(ConstFSEventStreamRef , - void *clientCallBackInfo, size_t numEvents, - void *eventPaths, - const FSEventStreamEventFlags eventFlags[], - const FSEventStreamEventId []) -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - QFSEventsFileSystemWatcherEngine *watcher = static_cast<QFSEventsFileSystemWatcherEngine *>(clientCallBackInfo); - QMutexLocker locker(&watcher->mutex); - CFArrayRef paths = static_cast<CFArrayRef>(eventPaths); - for (size_t i = 0; i < numEvents; ++i) { - const QString path = QCFString::toQString( - static_cast<CFStringRef>(CFArrayGetValueAtIndex(paths, i))); - const FSEventStreamEventFlags pathFlags = eventFlags[i]; - // There are several flags that may be passed, but we really don't care about them ATM. - // Here they are and why we don't care. - // kFSEventStreamEventFlagHistoryDone--(very unlikely to be gotten, but even then, not much changes). - // kFSEventStreamEventFlagMustScanSubDirs--Likely means the data is very much out of date, we - // aren't coalescing our directories, so again not so much of an issue - // kFSEventStreamEventFlagRootChanged | kFSEventStreamEventFlagMount | kFSEventStreamEventFlagUnmount-- - // These three flags indicate something has changed, but the stat will likely show this, so - // there's not really much to worry about. - // (btw, FSEvents is not the correct way of checking for mounts/unmounts, - // there are real CarbonCore events for that.) - Q_UNUSED(pathFlags); - if (watcher->filePathInfoHash.contains(path)) - watcher->updateList(watcher->filePathInfoHash[path], false, true); - - if (watcher->dirPathInfoHash.contains(path)) - watcher->updateList(watcher->dirPathInfoHash[path], true, true); - } -#else - Q_UNUSED(clientCallBackInfo); - Q_UNUSED(numEvents); - Q_UNUSED(eventPaths); - Q_UNUSED(eventFlags); -#endif -} - -void QFSEventsFileSystemWatcherEngine::stop() -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - QMutexLocker locker(&mutex); - stopFSStream(fsStream); - if (threadsRunLoop) { - CFRunLoopStop(threadsRunLoop); - waitForStop.wait(&mutex); - } -#endif -} - -void QFSEventsFileSystemWatcherEngine::updateFiles() -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - QMutexLocker locker(&mutex); - updateHash(filePathInfoHash); - updateHash(dirPathInfoHash); - if (filePathInfoHash.isEmpty() && dirPathInfoHash.isEmpty()) { - // Everything disappeared before we got to start, don't bother. -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - // Code duplicated from stop(), with the exception that we - // don't wait on waitForStop here. Doing this will lead to - // a deadlock since this function is called from the worker - // thread. (waitForStop.wakeAll() is only called from the - // end of run()). - stopFSStream(fsStream); - if (threadsRunLoop) - CFRunLoopStop(threadsRunLoop); -#endif - cleanupFSStream(fsStream); - } - waitCondition.wakeAll(); -#endif -} - -void QFSEventsFileSystemWatcherEngine::run() -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - threadsRunLoop = CFRunLoopGetCurrent(); - FSEventStreamScheduleWithRunLoop(fsStream, threadsRunLoop, kCFRunLoopDefaultMode); - bool startedOK = FSEventStreamStart(fsStream); - // It's recommended by Apple that you only update the files after you've started - // the stream, because otherwise you might miss an update in between starting it. - updateFiles(); -#ifdef QT_NO_DEBUG - Q_UNUSED(startedOK); -#else - Q_ASSERT(startedOK); -#endif - // If for some reason we called stop up above (and invalidated our stream), this call will return - // immediately. - CFRunLoopRun(); - threadsRunLoop = 0; - QMutexLocker locker(&mutex); - waitForStop.wakeAll(); -#endif -} - -QT_END_NAMESPACE -#endif //QT_NO_FILESYSTEMWATCHER |