Remove thread from QFileSystemWatcherEngine implementations.

These threads are actually counterproductive, as generally speaking, processing
watches is not that expensive an operation, so instead, they process at full
speed and can (in the case of slow processing in the thread processing the
events) stack up and consume resources for no good reason.

Threads also have an additional resource consumption per engine (some ~8mb of
thread stack on Linux), so doing away with them is nice.

A side effect of this change is that events are now effectively rate-limited by
the eventloop speed of the thread they run in, so if your thread runs too slow,
and you recieve a lot of events, on some platforms, events may be dropped now
where in the past, they would be read by the monitor thread and turned into Qt
signals (thus not visibly showing as a problem, apart from invisibly bloating
memory usage).

Task-number: QTBUG-20028
Change-Id: I345a56a8c709f6f778ca9a0b55b57c05229ba477
Reviewed-by: João Abecasis <joao.abecasis@nokia.com>
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
This commit is contained in:
Robin Burchell 2011-12-29 23:56:27 +01:00 committed by Qt by Nokia
parent 3e4f7ed5ed
commit d7ec8bf29a
10 changed files with 70 additions and 183 deletions

View File

@ -220,14 +220,10 @@ QFileSystemWatcher::~QFileSystemWatcher()
{ {
Q_D(QFileSystemWatcher); Q_D(QFileSystemWatcher);
if (d->native) { if (d->native) {
d->native->stop();
d->native->wait();
delete d->native; delete d->native;
d->native = 0; d->native = 0;
} }
if (d->poller) { if (d->poller) {
d->poller->stop();
d->poller->wait();
delete d->poller; delete d->poller;
d->poller = 0; d->poller = 0;
} }

View File

@ -226,10 +226,10 @@ QInotifyFileSystemWatcherEngine *QInotifyFileSystemWatcherEngine::create()
QInotifyFileSystemWatcherEngine::QInotifyFileSystemWatcherEngine(int fd) QInotifyFileSystemWatcherEngine::QInotifyFileSystemWatcherEngine(int fd)
: inotifyFd(fd) : inotifyFd(fd)
, notifier(fd, QSocketNotifier::Read, this)
{ {
fcntl(inotifyFd, F_SETFD, FD_CLOEXEC); fcntl(inotifyFd, F_SETFD, FD_CLOEXEC);
connect(&notifier, SIGNAL(activated(int)), SLOT(readFromInotify()));
moveToThread(this);
} }
QInotifyFileSystemWatcherEngine::~QInotifyFileSystemWatcherEngine() QInotifyFileSystemWatcherEngine::~QInotifyFileSystemWatcherEngine()
@ -240,13 +240,6 @@ QInotifyFileSystemWatcherEngine::~QInotifyFileSystemWatcherEngine()
::close(inotifyFd); ::close(inotifyFd);
} }
void QInotifyFileSystemWatcherEngine::run()
{
QSocketNotifier sn(inotifyFd, QSocketNotifier::Read, this);
connect(&sn, SIGNAL(activated(int)), SLOT(readFromInotify()));
(void) exec();
}
QStringList QInotifyFileSystemWatcherEngine::addPaths(const QStringList &paths, QStringList QInotifyFileSystemWatcherEngine::addPaths(const QStringList &paths,
QStringList *files, QStringList *files,
QStringList *directories) QStringList *directories)
@ -302,8 +295,6 @@ QStringList QInotifyFileSystemWatcherEngine::addPaths(const QStringList &paths,
idToPath.insert(id, path); idToPath.insert(id, path);
} }
start();
return p; return p;
} }
@ -337,11 +328,6 @@ QStringList QInotifyFileSystemWatcherEngine::removePaths(const QStringList &path
return p; return p;
} }
void QInotifyFileSystemWatcherEngine::stop()
{
quit();
}
void QInotifyFileSystemWatcherEngine::readFromInotify() void QInotifyFileSystemWatcherEngine::readFromInotify()
{ {
QMutexLocker locker(&mutex); QMutexLocker locker(&mutex);

View File

@ -57,8 +57,9 @@
#ifndef QT_NO_FILESYSTEMWATCHER #ifndef QT_NO_FILESYSTEMWATCHER
#include <qhash.h> #include <QtCore/qhash.h>
#include <qmutex.h> #include <QtCore/qmutex.h>
#include <QtCore/qsocketnotifier.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -71,13 +72,9 @@ public:
static QInotifyFileSystemWatcherEngine *create(); static QInotifyFileSystemWatcherEngine *create();
void run();
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories); QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories); QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
void stop();
private Q_SLOTS: private Q_SLOTS:
void readFromInotify(); void readFromInotify();
@ -87,6 +84,7 @@ private:
QMutex mutex; QMutex mutex;
QHash<QString, int> pathToID; QHash<QString, int> pathToID;
QHash<int, QString> idToPath; QHash<int, QString> idToPath;
QSocketNotifier notifier;
}; };

View File

@ -77,39 +77,16 @@ QKqueueFileSystemWatcherEngine *QKqueueFileSystemWatcherEngine::create()
QKqueueFileSystemWatcherEngine::QKqueueFileSystemWatcherEngine(int kqfd) QKqueueFileSystemWatcherEngine::QKqueueFileSystemWatcherEngine(int kqfd)
: kqfd(kqfd) : kqfd(kqfd)
, notifier(kqfd, QSocketNotifier::Read, this)
{ {
connect(&notifier, SIGNAL(activated(int)), SLOT(readFromKqueue()));
fcntl(kqfd, F_SETFD, FD_CLOEXEC); fcntl(kqfd, F_SETFD, FD_CLOEXEC);
if (pipe(kqpipe) == -1) {
perror("QKqueueFileSystemWatcherEngine: cannot create pipe");
kqpipe[0] = kqpipe[1] = -1;
return;
}
fcntl(kqpipe[0], F_SETFD, FD_CLOEXEC);
fcntl(kqpipe[1], F_SETFD, FD_CLOEXEC);
struct kevent kev;
EV_SET(&kev,
kqpipe[0],
EVFILT_READ,
EV_ADD | EV_ENABLE,
0,
0,
0);
if (kevent(kqfd, &kev, 1, 0, 0, 0) == -1) {
perror("QKqueueFileSystemWatcherEngine: cannot watch pipe, kevent returned");
return;
}
} }
QKqueueFileSystemWatcherEngine::~QKqueueFileSystemWatcherEngine() QKqueueFileSystemWatcherEngine::~QKqueueFileSystemWatcherEngine()
{ {
stop();
wait();
close(kqfd); close(kqfd);
close(kqpipe[0]);
close(kqpipe[1]);
foreach (int id, pathToID) foreach (int id, pathToID)
::close(id < 0 ? -id : id); ::close(id < 0 ? -id : id);
@ -192,11 +169,6 @@ QStringList QKqueueFileSystemWatcherEngine::addPaths(const QStringList &paths,
} }
} }
if (!isRunning())
start();
else
write(kqpipe[1], "@", 1);
return p; return p;
} }
@ -230,102 +202,66 @@ QStringList QKqueueFileSystemWatcherEngine::removePaths(const QStringList &paths
isEmpty = pathToID.isEmpty(); isEmpty = pathToID.isEmpty();
} }
if (isEmpty) {
stop();
wait();
} else {
write(kqpipe[1], "@", 1);
}
return p; return p;
} }
void QKqueueFileSystemWatcherEngine::stop() void QKqueueFileSystemWatcherEngine::readFromKqueue()
{
write(kqpipe[1], "q", 1);
}
void QKqueueFileSystemWatcherEngine::run()
{ {
forever { forever {
DEBUG() << "QKqueueFileSystemWatcherEngine: polling for changes";
int r; int r;
struct kevent kev; struct kevent kev;
DEBUG() << "QKqueueFileSystemWatcherEngine: waiting for kevents..."; struct timespec ts = { 0, 0 }; // 0 ts, because we want to poll
EINTR_LOOP(r, kevent(kqfd, 0, 0, &kev, 1, 0)); EINTR_LOOP(r, kevent(kqfd, 0, 0, &kev, 1, &ts));
if (r < 0) { if (r < 0) {
perror("QKqueueFileSystemWatcherEngine: error during kevent wait"); perror("QKqueueFileSystemWatcherEngine: error during kevent wait");
return; return;
} else if (r == 0) {
// polling returned no events, so stop
break;
} else { } else {
int fd = kev.ident; int fd = kev.ident;
DEBUG() << "QKqueueFileSystemWatcherEngine: processing kevent" << kev.ident << kev.filter; DEBUG() << "QKqueueFileSystemWatcherEngine: processing kevent" << kev.ident << kev.filter;
if (fd == kqpipe[0]) { QMutexLocker locker(&mutex);
// read all pending data from the pipe
QByteArray ba;
ba.resize(kev.data);
if (read(kqpipe[0], ba.data(), ba.size()) != ba.size()) {
perror("QKqueueFileSystemWatcherEngine: error reading from pipe");
return;
}
// read the command from the buffer (but break and return on 'q')
char cmd = 0;
for (int i = 0; i < ba.size(); ++i) {
cmd = ba.constData()[i];
if (cmd == 'q')
break;
}
// handle the command
switch (cmd) {
case 'q':
DEBUG() << "QKqueueFileSystemWatcherEngine: thread received 'q', exiting...";
return;
case '@':
DEBUG() << "QKqueueFileSystemWatcherEngine: thread received '@', continuing...";
break;
default:
DEBUG() << "QKqueueFileSystemWatcherEngine: thread received unknow message" << cmd;
break;
}
} else {
QMutexLocker locker(&mutex);
int id = fd; int id = fd;
QString path = idToPath.value(id); QString path = idToPath.value(id);
if (path.isEmpty()) {
// perhaps a directory?
id = -id;
path = idToPath.value(id);
if (path.isEmpty()) { if (path.isEmpty()) {
// perhaps a directory? DEBUG() << "QKqueueFileSystemWatcherEngine: received a kevent for a file we're not watching";
id = -id;
path = idToPath.value(id);
if (path.isEmpty()) {
DEBUG() << "QKqueueFileSystemWatcherEngine: received a kevent for a file we're not watching";
continue;
}
}
if (kev.filter != EVFILT_VNODE) {
DEBUG() << "QKqueueFileSystemWatcherEngine: received a kevent with the wrong filter";
continue; continue;
} }
}
if (kev.filter != EVFILT_VNODE) {
DEBUG() << "QKqueueFileSystemWatcherEngine: received a kevent with the wrong filter";
continue;
}
if ((kev.fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) != 0) { if ((kev.fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) != 0) {
DEBUG() << path << "removed, removing watch also"; DEBUG() << path << "removed, removing watch also";
pathToID.remove(path); pathToID.remove(path);
idToPath.remove(id); idToPath.remove(id);
::close(fd); ::close(fd);
if (id < 0) if (id < 0)
emit directoryChanged(path, true); emit directoryChanged(path, true);
else else
emit fileChanged(path, true); emit fileChanged(path, true);
} else { } else {
DEBUG() << path << "changed"; DEBUG() << path << "changed";
if (id < 0) if (id < 0)
emit directoryChanged(path, false); emit directoryChanged(path, false);
else else
emit fileChanged(path, false); emit fileChanged(path, false);
}
} }
} }
} }
} }

View File

@ -59,6 +59,7 @@
#include <QtCore/qmutex.h> #include <QtCore/qmutex.h>
#include <QtCore/qthread.h> #include <QtCore/qthread.h>
#include <QtCore/qvector.h> #include <QtCore/qvector.h>
#include <QtCore/qsocketnotifier.h>
#ifndef QT_NO_FILESYSTEMWATCHER #ifndef QT_NO_FILESYSTEMWATCHER
struct kevent; struct kevent;
@ -76,19 +77,18 @@ public:
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories); QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories); QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
void stop(); private Q_SLOTS:
void readFromKqueue();
private: private:
QKqueueFileSystemWatcherEngine(int kqfd); QKqueueFileSystemWatcherEngine(int kqfd);
void run();
int kqfd; int kqfd;
int kqpipe[2];
QMutex mutex; QMutex mutex;
QHash<QString, int> pathToID; QHash<QString, int> pathToID;
QHash<int, QString> idToPath; QHash<int, QString> idToPath;
QSocketNotifier notifier;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -60,19 +60,16 @@
#include <private/qobject_p.h> #include <private/qobject_p.h>
#include <QtCore/qstringlist.h> #include <QtCore/qstringlist.h>
#include <QtCore/qthread.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QFileSystemWatcherEngine : public QThread class QFileSystemWatcherEngine : public QObject
{ {
Q_OBJECT Q_OBJECT
protected: protected:
inline QFileSystemWatcherEngine(bool move = true) inline QFileSystemWatcherEngine()
{ {
if (move)
moveToThread(this);
} }
public: public:
@ -88,8 +85,6 @@ public:
QStringList *files, QStringList *files,
QStringList *directories) = 0; QStringList *directories) = 0;
virtual void stop() = 0;
Q_SIGNALS: Q_SIGNALS:
void fileChanged(const QString &path, bool removed); void fileChanged(const QString &path, bool removed);
void directoryChanged(const QString &path, bool removed); void directoryChanged(const QString &path, bool removed);

View File

@ -45,18 +45,9 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
QPollingFileSystemWatcherEngine::QPollingFileSystemWatcherEngine() QPollingFileSystemWatcherEngine::QPollingFileSystemWatcherEngine()
: timer(this)
{ {
#ifndef QT_NO_THREAD
moveToThread(this);
#endif
}
void QPollingFileSystemWatcherEngine::run()
{
QTimer timer;
connect(&timer, SIGNAL(timeout()), SLOT(timeout())); connect(&timer, SIGNAL(timeout()), SLOT(timeout()));
timer.start(PollingInterval);
(void) exec();
} }
QStringList QPollingFileSystemWatcherEngine::addPaths(const QStringList &paths, QStringList QPollingFileSystemWatcherEngine::addPaths(const QStringList &paths,
@ -84,7 +75,13 @@ QStringList QPollingFileSystemWatcherEngine::addPaths(const QStringList &paths,
} }
it.remove(); it.remove();
} }
start();
if ((!this->files.isEmpty() ||
!this->directories.isEmpty()) &&
!timer.isActive()) {
timer.start(PollingInterval);
}
return p; return p;
} }
@ -105,17 +102,13 @@ QStringList QPollingFileSystemWatcherEngine::removePaths(const QStringList &path
it.remove(); it.remove();
} }
} }
if (this->files.isEmpty() && this->directories.isEmpty()) {
locker.unlock();
stop();
wait();
}
return p;
}
void QPollingFileSystemWatcherEngine::stop() if (this->files.isEmpty() &&
{ this->directories.isEmpty()) {
quit(); timer.stop();
}
return p;
} }
void QPollingFileSystemWatcherEngine::timeout() void QPollingFileSystemWatcherEngine::timeout()

View File

@ -57,6 +57,7 @@
#include <QtCore/qmutex.h> #include <QtCore/qmutex.h>
#include <QtCore/qdatetime.h> #include <QtCore/qdatetime.h>
#include <QtCore/qdir.h> #include <QtCore/qdir.h>
#include <QtCore/qtimer.h>
#include "qfilesystemwatcher_p.h" #include "qfilesystemwatcher_p.h"
@ -110,15 +111,14 @@ class QPollingFileSystemWatcherEngine : public QFileSystemWatcherEngine
public: public:
QPollingFileSystemWatcherEngine(); QPollingFileSystemWatcherEngine();
void run();
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories); QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories); QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
void stop();
private Q_SLOTS: private Q_SLOTS:
void timeout(); void timeout();
private:
QTimer timer;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -53,22 +53,8 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
void QWindowsFileSystemWatcherEngine::stop()
{
foreach(QWindowsFileSystemWatcherEngineThread *thread, threads)
thread->stop();
}
QWindowsFileSystemWatcherEngine::QWindowsFileSystemWatcherEngine()
: QFileSystemWatcherEngine(false)
{
}
QWindowsFileSystemWatcherEngine::~QWindowsFileSystemWatcherEngine() QWindowsFileSystemWatcherEngine::~QWindowsFileSystemWatcherEngine()
{ {
if (threads.isEmpty())
return;
foreach(QWindowsFileSystemWatcherEngineThread *thread, threads) { foreach(QWindowsFileSystemWatcherEngineThread *thread, threads) {
thread->stop(); thread->stop();
thread->wait(); thread->wait();

View File

@ -60,6 +60,7 @@
#include <qt_windows.h> #include <qt_windows.h>
#include <QtCore/qdatetime.h> #include <QtCore/qdatetime.h>
#include <QtCore/qthread.h>
#include <QtCore/qfile.h> #include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h> #include <QtCore/qfileinfo.h>
#include <QtCore/qhash.h> #include <QtCore/qhash.h>
@ -78,15 +79,11 @@ class QWindowsFileSystemWatcherEngine : public QFileSystemWatcherEngine
{ {
Q_OBJECT Q_OBJECT
public: public:
QWindowsFileSystemWatcherEngine();
~QWindowsFileSystemWatcherEngine(); ~QWindowsFileSystemWatcherEngine();
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories); QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories); QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
void stop();
class Handle class Handle
{ {
public: public: