diff options
-rw-r--r-- | src/corelib/kernel/qcoreapplication.cpp | 28 | ||||
-rw-r--r-- | src/corelib/kernel/qcoreapplication_p.h | 2 | ||||
-rw-r--r-- | src/corelib/kernel/qeventloop.cpp | 2 | ||||
-rw-r--r-- | src/corelib/thread/qthread.cpp | 27 | ||||
-rw-r--r-- | src/corelib/thread/qthread_p.h | 10 | ||||
-rw-r--r-- | src/network/access/qnetworkreplyhttpimpl.cpp | 2 | ||||
-rw-r--r-- | src/network/bearer/qnetworkconfigmanager_p.cpp | 3 | ||||
-rw-r--r-- | src/platformsupport/input/evdevtablet/qevdevtablet.cpp | 2 | ||||
-rw-r--r-- | src/platformsupport/input/evdevtablet/qevdevtablet_p.h | 3 | ||||
-rw-r--r-- | src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp | 2 | ||||
-rw-r--r-- | src/platformsupport/input/evdevtouch/qevdevtouchhandler_p.h | 3 | ||||
-rw-r--r-- | src/widgets/kernel/qapplication.cpp | 4 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro | 1 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp | 50 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h | 2 |
15 files changed, 129 insertions, 12 deletions
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index e47dc0dff2..9ad7970807 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -538,6 +538,14 @@ QThread *QCoreApplicationPrivate::mainThread() return theMainThread; } +bool QCoreApplicationPrivate::threadRequiresCoreApplication() +{ + QThreadData *data = QThreadData::current(false); + if (!data) + return true; // default setting + return data->requiresCoreApplication; +} + void QCoreApplicationPrivate::checkReceiverThread(QObject *receiver) { QThread *currentThread = QThread::currentThread(); @@ -926,6 +934,8 @@ bool QCoreApplication::isQuitLockEnabled() return quitLockRefEnabled; } +static bool doNotify(QObject *, QEvent *); + /*! Enables the ability of the QEventLoopLocker feature to quit the application. @@ -960,7 +970,8 @@ bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event) */ bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event) { - if (!self) + bool selfRequired = QCoreApplicationPrivate::threadRequiresCoreApplication(); + if (!self && selfRequired) return false; // Make it possible for Qt Script to hook into events even @@ -978,6 +989,8 @@ bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event) QObjectPrivate *d = receiver->d_func(); QThreadData *threadData = d->threadData; QScopedLoopLevelCounter loopLevelCounter(threadData); + if (!selfRequired) + return doNotify(receiver, event); return self->notify(receiver, event); } @@ -1039,7 +1052,11 @@ bool QCoreApplication::notify(QObject *receiver, QEvent *event) // no events are delivered after ~QCoreApplication() has started if (QCoreApplicationPrivate::is_app_closing) return true; + return doNotify(receiver, event); +} +static bool doNotify(QObject *receiver, QEvent *event) +{ if (receiver == 0) { // serious error qWarning("QCoreApplication::notify: Unexpected null receiver"); return true; @@ -1054,7 +1071,10 @@ bool QCoreApplication::notify(QObject *receiver, QEvent *event) bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiver, QEvent *event) { - if (receiver->d_func()->threadData == this->threadData && extraData) { + // We can't access the application event filters outside of the main thread (race conditions) + Q_ASSERT(receiver->d_func()->threadData->thread == mainThread()); + + if (extraData) { // application event filters are only called for objects in the GUI thread for (int i = 0; i < extraData->eventFilters.size(); ++i) { QObject *obj = extraData->eventFilters.at(i); @@ -1097,7 +1117,9 @@ bool QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject *receiver, Q bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event) { // send to all application event filters (only does anything in the main thread) - if (QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event)) + if (QCoreApplication::self + && receiver->d_func()->threadData->thread == mainThread() + && QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event)) return true; // send to all receiver event filters if (sendThroughObjectEventFilters(receiver, event)) diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index 5fed850c2b..21f59d8197 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -107,6 +107,8 @@ public: static QThread *theMainThread; static QThread *mainThread(); + static bool threadRequiresCoreApplication(); + static void sendPostedEvents(QObject *receiver, int event_type, QThreadData *data); static void checkReceiverThread(QObject *receiver); diff --git a/src/corelib/kernel/qeventloop.cpp b/src/corelib/kernel/qeventloop.cpp index 1723db0ab9..dca25ce968 100644 --- a/src/corelib/kernel/qeventloop.cpp +++ b/src/corelib/kernel/qeventloop.cpp @@ -93,7 +93,7 @@ QEventLoop::QEventLoop(QObject *parent) : QObject(*new QEventLoopPrivate, parent) { Q_D(QEventLoop); - if (!QCoreApplication::instance()) { + if (!QCoreApplication::instance() && QCoreApplicationPrivate::threadRequiresCoreApplication()) { qWarning("QEventLoop: Cannot be used without QApplication"); } else if (!d->threadData->eventDispatcher.load()) { QThreadPrivate::createEventDispatcher(d->threadData); diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 0c009db930..590479d68c 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -50,7 +50,8 @@ QT_BEGIN_NAMESPACE QThreadData::QThreadData(int initialRefCount) : _ref(initialRefCount), loopLevel(0), thread(0), threadId(0), - eventDispatcher(0), quitNow(false), canWait(true), isAdopted(false) + eventDispatcher(0), + quitNow(false), canWait(true), isAdopted(false), requiresCoreApplication(true) { // fprintf(stderr, "QThreadData %p created\n", this); } @@ -867,4 +868,28 @@ bool QThread::isInterruptionRequested() const return d->interruptionRequested; } +/*! + \class QDaemonThread + \since 5.5 + \brief The QDaemonThread provides a class to manage threads that outlive QCoreApplication + \internal + + Note: don't try to deliver events from the started() signal. +*/ +static void setThreadDoesNotRequireCoreApplication() +{ + QThreadData::current()->requiresCoreApplication = false; +} + +QDaemonThread::QDaemonThread(QObject *parent) + : QThread(parent) +{ + // QThread::started() is emitted from the thread we start + connect(this, &QThread::started, setThreadDoesNotRequireCoreApplication); +} + +QDaemonThread::~QDaemonThread() +{ +} + QT_END_NAMESPACE diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index 1ecd682ad1..ffefe0b1d1 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -135,6 +135,13 @@ private: #ifndef QT_NO_THREAD +class Q_CORE_EXPORT QDaemonThread : public QThread +{ +public: + QDaemonThread(QObject *parent = 0); + ~QDaemonThread(); +}; + class QThreadPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QThread) @@ -224,7 +231,7 @@ public: QThreadData(int initialRefCount = 1); ~QThreadData(); - static QThreadData *current(bool createIfNecessary = true); + static Q_AUTOTEST_EXPORT QThreadData *current(bool createIfNecessary = true); static void clearCurrentThreadData(); static QThreadData *get2(QThread *thread) { Q_ASSERT_X(thread != 0, "QThread", "internal error"); return thread->d_func()->data; } @@ -278,6 +285,7 @@ public: bool quitNow; bool canWait; bool isAdopted; + bool requiresCoreApplication; }; class QScopedLoopLevelCounter diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index c08648c47b..8c0c098147 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -49,6 +49,8 @@ #include "qthread.h" #include "QtCore/qcoreapplication.h" +#include <QtCore/private/qthread_p.h> + #include "qnetworkcookiejar.h" #ifndef QT_NO_HTTP diff --git a/src/network/bearer/qnetworkconfigmanager_p.cpp b/src/network/bearer/qnetworkconfigmanager_p.cpp index c1e837de7b..78d4970ca1 100644 --- a/src/network/bearer/qnetworkconfigmanager_p.cpp +++ b/src/network/bearer/qnetworkconfigmanager_p.cpp @@ -41,6 +41,7 @@ #include <QtCore/qstringlist.h> #include <QtCore/qthread.h> #include <QtCore/private/qcoreapplication_p.h> +#include <QtCore/private/qthread_p.h> #ifndef QT_NO_BEARERMANAGEMENT @@ -61,7 +62,7 @@ QNetworkConfigurationManagerPrivate::QNetworkConfigurationManagerPrivate() void QNetworkConfigurationManagerPrivate::initialize() { //Two stage construction, because we only want to do this heavyweight work for the winner of the Q_GLOBAL_STATIC race. - bearerThread = new QThread(); + bearerThread = new QDaemonThread(); bearerThread->setObjectName(QStringLiteral("Qt bearer thread")); bearerThread->moveToThread(QCoreApplicationPrivate::mainThread()); // because cleanup() is called in main thread context. diff --git a/src/platformsupport/input/evdevtablet/qevdevtablet.cpp b/src/platformsupport/input/evdevtablet/qevdevtablet.cpp index c59f0f390a..c6f952c64d 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtablet.cpp +++ b/src/platformsupport/input/evdevtablet/qevdevtablet.cpp @@ -292,7 +292,7 @@ void QEvdevTabletHandler::readData() QEvdevTabletHandlerThread::QEvdevTabletHandlerThread(const QString &spec, QObject *parent) - : QThread(parent), m_spec(spec), m_handler(0) + : QDaemonThread(parent), m_spec(spec), m_handler(0) { start(); } diff --git a/src/platformsupport/input/evdevtablet/qevdevtablet_p.h b/src/platformsupport/input/evdevtablet/qevdevtablet_p.h index f9682290d9..f546f9a88a 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtablet_p.h +++ b/src/platformsupport/input/evdevtablet/qevdevtablet_p.h @@ -48,6 +48,7 @@ #include <QObject> #include <QString> #include <QThread> +#include <QtCore/private/qthread_p.h> QT_BEGIN_NAMESPACE @@ -68,7 +69,7 @@ private: QEvdevTabletData *d; }; -class QEvdevTabletHandlerThread : public QThread +class QEvdevTabletHandlerThread : public QDaemonThread { public: explicit QEvdevTabletHandlerThread(const QString &spec, QObject *parent = 0); diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp index 1556be5cc1..1ca95074b9 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp +++ b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp @@ -642,7 +642,7 @@ void QEvdevTouchScreenData::reportPoints() QEvdevTouchScreenHandlerThread::QEvdevTouchScreenHandlerThread(const QString &device, const QString &spec, QObject *parent) - : QThread(parent), m_device(device), m_spec(spec), m_handler(0) + : QDaemonThread(parent), m_device(device), m_spec(spec), m_handler(0) { start(); } diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchhandler_p.h b/src/platformsupport/input/evdevtouch/qevdevtouchhandler_p.h index a6d3a860f5..8e7dfe59bb 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchhandler_p.h +++ b/src/platformsupport/input/evdevtouch/qevdevtouchhandler_p.h @@ -49,6 +49,7 @@ #include <QString> #include <QList> #include <QThread> +#include <QtCore/private/qthread_p.h> #include <qpa/qwindowsysteminterface.h> #if !defined(QT_NO_MTDEV) @@ -80,7 +81,7 @@ private: #endif }; -class QEvdevTouchScreenHandlerThread : public QThread +class QEvdevTouchScreenHandlerThread : public QDaemonThread { public: explicit QEvdevTouchScreenHandlerThread(const QString &device, const QString &spec, QObject *parent = 0); diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 504932dc53..f82fac836a 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -3688,7 +3688,9 @@ bool QApplication::notify(QObject *receiver, QEvent *e) bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e) { // send to all application event filters - if (sendThroughApplicationEventFilters(receiver, e)) + if (threadRequiresCoreApplication() + && receiver->d_func()->threadData->thread == mainThread() + && sendThroughApplicationEventFilters(receiver, e)) return true; if (receiver->isWidgetType()) { diff --git a/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro b/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro index 0602b9fc38..1039f2c08d 100644 --- a/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro +++ b/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro @@ -3,3 +3,4 @@ TARGET = tst_qcoreapplication QT = core testlib core-private SOURCES = tst_qcoreapplication.cpp HEADERS = tst_qcoreapplication.h +requires(contains(QT_CONFIG,private_tests)) diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp index efecf31d66..696a1c4722 100644 --- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp +++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp @@ -78,6 +78,21 @@ public: } }; +class Thread : public QDaemonThread +{ + void run() Q_DECL_OVERRIDE + { + QThreadData *data = QThreadData::current(); + QVERIFY(!data->requiresCoreApplication); // daemon thread + data->requiresCoreApplication = requiresCoreApplication; + QThread::run(); + } + +public: + Thread() : requiresCoreApplication(true) {} + bool requiresCoreApplication; +}; + void tst_QCoreApplication::sendEventsOnProcessEvents() { int argc = 1; @@ -853,6 +868,41 @@ void tst_QCoreApplication::applicationEventFilters_auxThread() QVERIFY(!spy.recordedEvents.contains(QEvent::User + 1)); } +void tst_QCoreApplication::threadedEventDelivery_data() +{ + QTest::addColumn<bool>("requiresCoreApplication"); + QTest::addColumn<bool>("createCoreApplication"); + QTest::addColumn<bool>("eventsReceived"); + + // invalid combination: + //QTest::newRow("default-without-coreapp") << true << false << false; + QTest::newRow("default") << true << true << true; + QTest::newRow("independent-without-coreapp") << false << false << true; + QTest::newRow("independent-with-coreapp") << false << true << true; +} + +// posts the event before the QCoreApplication is destroyed, starts thread after +void tst_QCoreApplication::threadedEventDelivery() +{ + QFETCH(bool, requiresCoreApplication); + QFETCH(bool, createCoreApplication); + QFETCH(bool, eventsReceived); + + int argc = 1; + char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; + QScopedPointer<TestApplication> app(createCoreApplication ? new TestApplication(argc, argv) : 0); + + Thread thread; + thread.requiresCoreApplication = requiresCoreApplication; + ThreadedEventReceiver receiver; + receiver.moveToThread(&thread); + QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User + 1))); + + thread.start(); + QVERIFY(thread.wait(1000)); + QCOMPARE(receiver.recordedEvents.contains(QEvent::User + 1), eventsReceived); +} + static void createQObjectOnDestruction() { // Make sure that we can create a QObject after the last QObject has been diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h index 09e15723ac..8efe374a2b 100644 --- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h +++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h @@ -61,6 +61,8 @@ private slots: void QTBUG31606_QEventDestructorDeadLock(); void applicationEventFilters_mainThread(); void applicationEventFilters_auxThread(); + void threadedEventDelivery_data(); + void threadedEventDelivery(); }; #endif // TST_QCOREAPPLICATION_H |