summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp36
-rw-r--r--src/corelib/kernel/qcoreapplication.h6
-rw-r--r--src/corelib/kernel/qcoreapplication_p.h4
-rw-r--r--src/corelib/kernel/qeventloop.cpp105
-rw-r--r--src/corelib/kernel/qeventloop.h17
-rw-r--r--src/corelib/kernel/qeventloop_p.h83
-rw-r--r--src/gui/kernel/qguiapplication.cpp11
-rw-r--r--src/gui/kernel/qguiapplication_p.h2
-rw-r--r--src/gui/kernel/qwindow.cpp20
-rw-r--r--src/widgets/kernel/qapplication.cpp20
-rw-r--r--src/widgets/kernel/qapplication_p.h2
-rw-r--r--src/widgets/kernel/qwidget.cpp19
-rw-r--r--tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro2
-rw-r--r--tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp114
-rw-r--r--tests/auto/corelib/kernel/qeventloop/qeventloop.pro2
-rw-r--r--tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp82
-rw-r--r--tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp516
17 files changed, 979 insertions, 62 deletions
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index ae30167a42..a248e18a6a 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -271,6 +271,8 @@ struct QCoreApplicationData {
Q_GLOBAL_STATIC(QCoreApplicationData, coreappdata)
+static bool quitLockRefEnabled = true;
+
QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint flags)
: QObjectPrivate(), argc(aargc), argv(aargv), application_type(0), eventFilter(0),
in_exec(false), aboutToQuitEmitted(false), threadData_clean(false)
@@ -649,6 +651,29 @@ bool QCoreApplication::testAttribute(Qt::ApplicationAttribute attribute)
return QCoreApplicationPrivate::testAttribute(attribute);
}
+/*!/
+ Returns true if the use of the QEventLoopLocker feature can cause the
+ application to quit, otherwise returns false.
+
+ \sa QEventLoopLocker
+ */
+bool QCoreApplication::isQuitLockEnabled()
+{
+ return quitLockRefEnabled;
+}
+
+/*!
+ Enables the ability of the QEventLoopLocker feature to quit
+ the application.
+
+ If disabled, the use of QEventLoopLocker will not quit the application.
+
+ \sa QEventLoopLocker
+ */
+void QCoreApplication::setQuitLockEnabled(bool enabled)
+{
+ quitLockRefEnabled = enabled;
+}
/*!
\internal
@@ -1476,6 +1501,17 @@ bool QCoreApplication::event(QEvent *e)
\sa QObject::tr(), QObject::trUtf8(), QString::fromUtf8()
*/
+void QCoreApplicationPrivate::ref()
+{
+ quitLockRef.ref();
+}
+
+void QCoreApplicationPrivate::deref()
+{
+ if (!quitLockRef.deref() && in_exec && quitLockRefEnabled)
+ QCoreApplication::postEvent(qApp, new QEvent(QEvent::Quit));
+}
+
/*!
Tells the application to exit with return code 0 (success).
Equivalent to calling QCoreApplication::exit(0).
diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h
index 45583a67fa..d1fba3b63c 100644
--- a/src/corelib/kernel/qcoreapplication.h
+++ b/src/corelib/kernel/qcoreapplication.h
@@ -71,6 +71,7 @@ class Q_CORE_EXPORT QCoreApplication : public QObject
Q_PROPERTY(QString applicationVersion READ applicationVersion WRITE setApplicationVersion)
Q_PROPERTY(QString organizationName READ organizationName WRITE setOrganizationName)
Q_PROPERTY(QString organizationDomain READ organizationDomain WRITE setOrganizationDomain)
+ Q_PROPERTY(bool quitLockEnabled READ isQuitLockEnabled WRITE setQuitLockEnabled)
Q_DECLARE_PRIVATE(QCoreApplication)
public:
@@ -160,6 +161,9 @@ public:
EventFilter setEventFilter(EventFilter filter);
bool filterEvent(void *message, long *result);
+ static bool isQuitLockEnabled();
+ static void setQuitLockEnabled(bool enabled);
+
public Q_SLOTS:
static void quit();
@@ -183,7 +187,7 @@ private:
void init();
static QCoreApplication *self;
-
+
Q_DISABLE_COPY(QCoreApplication)
friend class QEventDispatcherUNIXPrivate;
diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h
index b5b6c0d5f2..861a046f16 100644
--- a/src/corelib/kernel/qcoreapplication_p.h
+++ b/src/corelib/kernel/qcoreapplication_p.h
@@ -89,6 +89,10 @@ public:
static QString macMenuBarName();
#endif
+ QAtomicInt quitLockRef;
+ void ref();
+ void deref();
+
static QThread *theMainThread;
static QThread *mainThread();
static bool checkInstance(const char *method);
diff --git a/src/corelib/kernel/qeventloop.cpp b/src/corelib/kernel/qeventloop.cpp
index 0c1b8c77ff..8b0ec85679 100644
--- a/src/corelib/kernel/qeventloop.cpp
+++ b/src/corelib/kernel/qeventloop.cpp
@@ -43,24 +43,15 @@
#include "qabstracteventdispatcher.h"
#include "qcoreapplication.h"
+#include "qcoreapplication_p.h"
#include "qelapsedtimer.h"
#include "qobject_p.h"
+#include "qeventloop_p.h"
#include <private/qthread_p.h>
QT_BEGIN_NAMESPACE
-class QEventLoopPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QEventLoop)
-public:
- inline QEventLoopPrivate()
- : exit(true), inExec(false), returnCode(-1)
- { }
- bool exit, inExec;
- int returnCode;
-};
-
/*!
\class QEventLoop
\brief The QEventLoop class provides a means of entering and leaving an event loop.
@@ -305,6 +296,17 @@ void QEventLoop::wakeUp()
d->threadData->eventDispatcher->wakeUp();
}
+
+bool QEventLoop::event(QEvent *event)
+{
+ if (event->type() == QEvent::Quit) {
+ quit();
+ return true;
+ } else {
+ return QObject::event(event);
+ }
+}
+
/*!
Tells the event loop to exit normally.
@@ -315,4 +317,85 @@ void QEventLoop::wakeUp()
void QEventLoop::quit()
{ exit(0); }
+
+class QEventLoopLockerPrivate
+{
+public:
+ explicit QEventLoopLockerPrivate(QEventLoopPrivate *loop)
+ : loop(loop), app(0)
+ {
+ loop->ref();
+ }
+
+ explicit QEventLoopLockerPrivate(QCoreApplicationPrivate *app)
+ : loop(0), app(app)
+ {
+ app->ref();
+ }
+
+ ~QEventLoopLockerPrivate()
+ {
+ if (loop)
+ loop->deref();
+ else
+ app->deref();
+ }
+
+private:
+ QEventLoopPrivate *loop;
+ QCoreApplicationPrivate *app;
+};
+
+/*!
+ \class QEventLoopLocker
+ \brief The QEventLoopLocker class provides a means to quit an event loop when it is no longer needed.
+
+ The QEventLoopLocker operates on particular objects - either a QCoreApplication
+ instance or a QEventLoop instance.
+
+ This makes it possible to, for example, run a batch of jobs with an event loop
+ and exit that event loop after the last job is finished. That is accomplished
+ by keeping a QEventLoopLocker with each job instance.
+
+ The variant which operates on QCoreApplication makes it possible to finish
+ asynchronously running jobs after the last gui window has been closed. This
+ can be useful for example for running a job which uploads data to a network.
+
+ \sa QEventLoop, QCoreApplication
+*/
+
+/*!
+ Creates an event locker operating on the \p app.
+
+ The application will quit when there are no more QEventLoopLockers operating on it.
+
+ \sa QCoreApplication::quit(), QCoreApplication::isQuitLockEnabled()
+ */
+QEventLoopLocker::QEventLoopLocker()
+ : d_ptr(new QEventLoopLockerPrivate(static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(QCoreApplication::instance()))))
+{
+
+}
+
+/*!
+ Creates an event locker operating on the \p app.
+
+ This particular QEventLoop will quit when there are no more QEventLoopLockers operating on it.
+
+ \sa QEventLoop::quit()
+ */
+QEventLoopLocker::QEventLoopLocker(QEventLoop *loop)
+ : d_ptr(new QEventLoopLockerPrivate(static_cast<QEventLoopPrivate*>(QObjectPrivate::get(loop))))
+{
+
+}
+
+/*!
+ Destroys this event loop locker object
+ */
+QEventLoopLocker::~QEventLoopLocker()
+{
+ delete d_ptr;
+}
+
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qeventloop.h b/src/corelib/kernel/qeventloop.h
index 3f5cce222f..0e7195d6a7 100644
--- a/src/corelib/kernel/qeventloop.h
+++ b/src/corelib/kernel/qeventloop.h
@@ -80,12 +80,29 @@ public:
void wakeUp();
+ bool event(QEvent *event);
+
public Q_SLOTS:
void quit();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QEventLoop::ProcessEventsFlags)
+
+class QEventLoopLockerPrivate;
+
+class Q_CORE_EXPORT QEventLoopLocker
+{
+public:
+ QEventLoopLocker();
+ explicit QEventLoopLocker(QEventLoop *loop);
+ ~QEventLoopLocker();
+
+private:
+ Q_DISABLE_COPY(QEventLoopLocker)
+ QEventLoopLockerPrivate *d_ptr;
+};
+
QT_END_NAMESPACE
QT_END_HEADER
diff --git a/src/corelib/kernel/qeventloop_p.h b/src/corelib/kernel/qeventloop_p.h
new file mode 100644
index 0000000000..5770ed0076
--- /dev/null
+++ b/src/corelib/kernel/qeventloop_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QEVENTLOOP_P_H
+#define QEVENTLOOP_P_H
+
+#include "qobject_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QEventLoopPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QEventLoop)
+public:
+ inline QEventLoopPrivate()
+ : exit(true), inExec(false), returnCode(-1)
+ { }
+
+ QAtomicInt quitLockRef;
+
+ bool exit, inExec;
+ int returnCode;
+
+ void ref()
+ {
+ quitLockRef.ref();
+ }
+
+ void deref()
+ {
+ if (!quitLockRef.deref() && inExec) {
+ qApp->postEvent(q_ptr, new QEvent(QEvent::Quit));
+ }
+ }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QEVENTLOOP_P_H
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index ea562c75b8..9f7bc24119 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -107,8 +107,6 @@ int QGuiApplicationPrivate::mousePressX = 0;
int QGuiApplicationPrivate::mousePressY = 0;
int QGuiApplicationPrivate::mouse_double_click_distance = 5;
-bool QGuiApplicationPrivate::quitOnLastWindowClosed = true;
-
static Qt::LayoutDirection layout_direction = Qt::LeftToRight;
static bool force_reverse = false;
@@ -1388,14 +1386,14 @@ void QGuiApplicationPrivate::notifyActiveWindowChange(QWindow *)
void QGuiApplication::setQuitOnLastWindowClosed(bool quit)
{
- QGuiApplicationPrivate::quitOnLastWindowClosed = quit;
+ QCoreApplication::setQuitLockEnabled(quit);
}
bool QGuiApplication::quitOnLastWindowClosed()
{
- return QGuiApplicationPrivate::quitOnLastWindowClosed;
+ return QCoreApplication::isQuitLockEnabled();
}
@@ -1403,11 +1401,6 @@ bool QGuiApplication::quitOnLastWindowClosed()
void QGuiApplicationPrivate::emitLastWindowClosed()
{
if (qGuiApp && qGuiApp->d_func()->in_exec) {
- if (QGuiApplicationPrivate::quitOnLastWindowClosed) {
- // get ready to quit, this event might be removed if the
- // event loop is re-entered, however
- QGuiApplication::postEvent(qApp, new QEvent(QEvent::Quit));
- }
emit qGuiApp->lastWindowClosed();
}
}
diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h
index 6d3e650c32..2f1cfa509d 100644
--- a/src/gui/kernel/qguiapplication_p.h
+++ b/src/gui/kernel/qguiapplication_p.h
@@ -183,8 +183,6 @@ public:
QStyleHints *styleHints;
QInputPanel *inputPanel;
- static bool quitOnLastWindowClosed;
-
static QList<QObject *> generic_plugin_list;
#ifndef QT_NO_SHORTCUT
QShortcutMap shortcutMap;
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index e85d8379fa..4436884359 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -165,6 +165,14 @@ void QWindow::setVisible(bool visible)
return;
d->visible = visible;
emit visibleChanged(visible);
+ if (QCoreApplication::instance() && !transientParent()) {
+ QCoreApplicationPrivate *applicationPrivate = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(QCoreApplication::instance()));
+ if (visible) {
+ applicationPrivate->ref();
+ } else {
+ applicationPrivate->deref();
+ }
+ }
if (!d->platformWindow)
create();
@@ -499,7 +507,19 @@ void QWindow::setWindowState(Qt::WindowState state)
void QWindow::setTransientParent(QWindow *parent)
{
Q_D(QWindow);
+
+ QWindow *previousParent = d->transientParent;
+
d->transientParent = parent;
+
+ if (QCoreApplication::instance() && d->visible) {
+ QCoreApplicationPrivate *applicationPrivate = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(QCoreApplication::instance()));
+ if (parent && !previousParent) {
+ applicationPrivate->deref();
+ } else if (!parent && previousParent) {
+ applicationPrivate->ref();
+ }
+ }
}
QWindow *QWindow::transientParent() const
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index f4d2856dbe..b0781c2064 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -143,8 +143,6 @@ QApplicationPrivate *QApplicationPrivate::self = 0;
QInputContext *QApplicationPrivate::inputContext = 0;
-bool QApplicationPrivate::quitOnLastWindowClosed = true;
-
#ifdef Q_WS_WINCE
int QApplicationPrivate::autoMaximizeThreshold = -1;
bool QApplicationPrivate::autoSipEnabled = false;
@@ -161,8 +159,6 @@ QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, QApplication::T
is_session_restored = false;
#endif
- quitOnLastWindowClosed = true;
-
#if defined(Q_WS_QWS) && !defined(QT_NO_DIRECTPAINTER)
directPainters = 0;
#endif
@@ -4621,24 +4617,12 @@ bool QApplicationPrivate::inPopupMode() const
void QApplication::setQuitOnLastWindowClosed(bool quit)
{
- QApplicationPrivate::quitOnLastWindowClosed = quit;
+ QCoreApplication::setQuitLockEnabled(quit);
}
bool QApplication::quitOnLastWindowClosed()
{
- return QApplicationPrivate::quitOnLastWindowClosed;
-}
-
-void QApplicationPrivate::emitLastWindowClosed()
-{
- if (qApp && qApp->d_func()->in_exec) {
- if (QApplicationPrivate::quitOnLastWindowClosed) {
- // get ready to quit, this event might be removed if the
- // event loop is re-entered, however
- QApplication::postEvent(qApp, new QEvent(QEvent::Quit));
- }
- emit qApp->lastWindowClosed();
- }
+ return QCoreApplication::isQuitLockEnabled();
}
/*! \variable QApplication::NormalColors
diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h
index eccc1b8442..b6fbfa1684 100644
--- a/src/widgets/kernel/qapplication_p.h
+++ b/src/widgets/kernel/qapplication_p.h
@@ -188,8 +188,6 @@ public:
static bool qws_apply_settings();
static QWidget *findWidget(const QObjectList&, const QPoint &, bool rec);
#endif
- static bool quitOnLastWindowClosed;
- static void emitLastWindowClosed();
#ifdef Q_WS_WINCE
static int autoMaximizeThreshold;
#endif
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index a8f3808dce..e004e15252 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -92,6 +92,7 @@
#include <private/qpaintengine_raster_p.h>
#include "qwidget_p.h"
+#include <QtGui/private/qwindow_p.h>
#include "qaction_p.h"
#include "qlayout_p.h"
#include "QtWidgets/qgraphicsproxywidget.h"
@@ -7434,23 +7435,11 @@ bool QWidgetPrivate::close_helper(CloseMode mode)
// Attempt to close the application only if this has WA_QuitOnClose set and a non-visible parent
quitOnClose = quitOnClose && (parentWidget.isNull() || !parentWidget->isVisible());
- if (quitOnClose) {
- /* if there is no non-withdrawn primary window left (except
- the ones without QuitOnClose), we emit the lastWindowClosed
- signal */
- QWidgetList list = QApplication::topLevelWidgets();
- bool lastWindowClosed = true;
- for (int i = 0; i < list.size(); ++i) {
- QWidget *w = list.at(i);
- if (!w->isVisible() || w->parentWidget() || !w->testAttribute(Qt::WA_QuitOnClose))
- continue;
- lastWindowClosed = false;
- break;
- }
- if (lastWindowClosed)
- QApplicationPrivate::emitLastWindowClosed();
+ if (quitOnClose && q->windowHandle()) {
+ static_cast<QWindowPrivate*>(QObjectPrivate::get(q->windowHandle()))->maybeQuitOnLastWindowClosed();
}
+
if (!that.isNull()) {
data.is_closing = 0;
if (q->testAttribute(Qt::WA_DeleteOnClose)) {
diff --git a/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro b/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro
index f9a38c2f14..14df20c986 100644
--- a/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro
+++ b/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro
@@ -1,4 +1,4 @@
CONFIG += testcase parallel_test
TARGET = tst_qcoreapplication
-QT = core testlib
+QT = core testlib core-private
SOURCES = tst_qcoreapplication.cpp
diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp
index fc3d8e0fbb..97c9757107 100644
--- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp
+++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp
@@ -39,10 +39,13 @@
**
****************************************************************************/
-
#include <QtCore/QtCore>
#include <QtTest/QtTest>
+#include <private/qcoreapplication_p.h>
+#include <private/qeventloop_p.h>
+#include <private/qthread_p.h>
+
class tst_QCoreApplication: public QObject
{
Q_OBJECT
@@ -63,6 +66,7 @@ private slots:
void execAfterExit();
void eventLoopExecAfterExit();
void customEventDispatcher();
+ void testQuitLock();
};
class EventSpy : public QObject
@@ -640,5 +644,113 @@ void tst_QCoreApplication::customEventDispatcher()
QVERIFY(weak_ed.isNull());
}
+class JobObject : public QObject
+{
+ Q_OBJECT
+public:
+
+ explicit JobObject(QEventLoop *loop, QObject *parent = 0)
+ : QObject(parent), locker(loop)
+ {
+ QTimer::singleShot(1000, this, SLOT(timeout()));
+ }
+
+ explicit JobObject(QObject *parent = 0)
+ : QObject(parent)
+ {
+ QTimer::singleShot(1000, this, SLOT(timeout()));
+ }
+
+public slots:
+ void startSecondaryJob()
+ {
+ new JobObject();
+ }
+
+private slots:
+ void timeout()
+ {
+ emit done();
+ deleteLater();
+ }
+
+signals:
+ void done();
+
+private:
+ QEventLoopLocker locker;
+};
+
+class QuitTester : public QObject
+{
+ Q_OBJECT
+public:
+ QuitTester(QObject *parent = 0)
+ : QObject(parent)
+ {
+ QTimer::singleShot(0, this, SLOT(doTest()));
+ }
+
+private slots:
+ void doTest()
+ {
+ QCoreApplicationPrivate *privateClass = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp));
+
+ {
+ QCOMPARE(privateClass->quitLockRef.load(), 0);
+ // Test with a lock active so that the refcount doesn't drop to zero during these tests, causing a quit.
+ // (until we exit the scope)
+ QEventLoopLocker locker;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ JobObject *job1 = new JobObject(this);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ delete job1;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ job1 = new JobObject(this);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ JobObject *job2 = new JobObject(this);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 3);
+
+ delete job1;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ JobObject *job3 = new JobObject(job2);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 3);
+
+ JobObject *job4 = new JobObject(job2);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 4);
+
+ delete job2;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ }
+ QCOMPARE(privateClass->quitLockRef.load(), 0);
+ }
+};
+
+void tst_QCoreApplication::testQuitLock()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+
+ QuitTester tester;
+ app.exec();
+}
+
+
QTEST_APPLESS_MAIN(tst_QCoreApplication)
#include "tst_qcoreapplication.moc"
diff --git a/tests/auto/corelib/kernel/qeventloop/qeventloop.pro b/tests/auto/corelib/kernel/qeventloop/qeventloop.pro
index d21a7d64f6..adfc810788 100644
--- a/tests/auto/corelib/kernel/qeventloop/qeventloop.pro
+++ b/tests/auto/corelib/kernel/qeventloop/qeventloop.pro
@@ -1,6 +1,6 @@
CONFIG += testcase
TARGET = tst_qeventloop
-QT = core network testlib
+QT = core network testlib core-private
SOURCES = tst_qeventloop.cpp
win32:!wince*:LIBS += -luser32
diff --git a/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp b/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp
index 897cba1f17..e2144134d9 100644
--- a/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp
+++ b/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp
@@ -45,6 +45,7 @@
#include <qcoreapplication.h>
#include <qcoreevent.h>
#include <qeventloop.h>
+#include <private/qeventloop_p.h>
#include <qmutex.h>
#include <qthread.h>
#include <qtimer.h>
@@ -194,6 +195,8 @@ private slots:
// keep this test last:
void nestedLoops();
+ void testQuitLock();
+
protected:
void customEvent(QEvent *e);
};
@@ -640,6 +643,85 @@ void tst_QEventLoop::deliverInDefinedOrder()
}
+class JobObject : public QObject
+{
+ Q_OBJECT
+public:
+
+ explicit JobObject(QEventLoop *loop, QObject *parent = 0)
+ : QObject(parent), loop(loop), locker(loop)
+ {
+ }
+
+ explicit JobObject(QObject *parent = 0)
+ : QObject(parent)
+ {
+ }
+
+public slots:
+ void start(int timeout = 200)
+ {
+ QTimer::singleShot(timeout, this, SLOT(timeout()));
+ }
+
+private slots:
+ void timeout()
+ {
+ emit done();
+ deleteLater();
+ }
+
+signals:
+ void done();
+
+private:
+ QEventLoop *loop;
+ QEventLoopLocker locker;
+};
+
+void tst_QEventLoop::testQuitLock()
+{
+ QEventLoop eventLoop;
+
+ QTimer timer;
+ timer.setInterval(100);
+ QSignalSpy timerSpy(&timer, SIGNAL(timeout()));
+ timer.start();
+
+ QEventLoopPrivate* privateClass = static_cast<QEventLoopPrivate*>(QObjectPrivate::get(&eventLoop));
+
+ QCOMPARE(privateClass->quitLockRef.load(), 0);
+
+ JobObject *job1 = new JobObject(&eventLoop, this);
+ job1->start(500);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ eventLoop.exec();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 0);
+
+ // The job takes long enough that the timer times out several times.
+ QVERIFY(timerSpy.count() > 3);
+ timerSpy.clear();
+
+ job1 = new JobObject(&eventLoop, this);
+ job1->start(200);
+
+ JobObject *previousJob = job1;
+ for (int i = 0; i < 9; ++i) {
+ JobObject *subJob = new JobObject(&eventLoop, this);
+ connect(previousJob, SIGNAL(done()), subJob, SLOT(start()));
+ previousJob = subJob;
+ }
+
+ eventLoop.exec();
+
+ qDebug() << timerSpy.count();
+ // The timer times out more if it has more subjobs to do.
+ // We run 10 jobs in sequence here of about 200ms each.
+ QVERIFY(timerSpy.count() > 17);
+}
QTEST_MAIN(tst_QEventLoop)
#include "tst_qeventloop.moc"
diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
index ac8f2a34ce..4b8034c7c7 100644
--- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
+++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
@@ -49,6 +49,7 @@
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtCore/QProcess>
+#include <QtCore/private/qeventloop_p.h>
#include <QtGui/QFontDatabase>
#include <QtGui/QClipboard>
@@ -138,6 +139,16 @@ private slots:
void qtbug_12673();
+ void testQuitLockRef();
+ void testQuitLock1();
+ void testQuitLock2();
+ void testQuitLock3();
+ void testQuitLock4();
+ void testQuitLock5();
+ void testQuitLock6();
+ void testQuitLock7();
+ void testQuitLock8();
+
void globalStaticObjectDestruction(); // run this last
};
@@ -189,7 +200,6 @@ public:
dialog.show();
dialog.close();
- hide();
event->ignore();
}
};
@@ -516,6 +526,7 @@ void tst_QApplication::lastWindowClosed()
QPointer<CloseWidget>widget = new CloseWidget;
QVERIFY(widget->testAttribute(Qt::WA_QuitOnClose));
+ widget->show();
QObject::connect(&app, SIGNAL(lastWindowClosed()), widget, SLOT(deleteLater()));
app.exec();
QVERIFY(!widget);
@@ -2048,6 +2059,509 @@ void tst_QApplication::qtbug_12673()
QCOMPARE(testProcess.exitStatus(), QProcess::NormalExit);
}
+class JobObject : public QObject
+{
+ Q_OBJECT
+public:
+ JobObject(int milliseconds, QObject *parent = 0)
+ : QObject(parent)
+ {
+ QTimer::singleShot(milliseconds, this, SLOT(timeout()));
+ }
+
+ JobObject(QObject *parent = 0)
+ : QObject(parent)
+ {
+ QTimer::singleShot(1000, this, SLOT(timeout()));
+ }
+
+private slots:
+ void timeout()
+ {
+ emit done();
+ deleteLater();
+ }
+
+signals:
+ void done();
+
+private:
+ QEventLoopLocker locker;
+};
+
+class QuitLockRefTester : public QObject
+{
+ Q_OBJECT
+public:
+ QuitLockRefTester(QObject *parent = 0)
+ : QObject(parent)
+ {
+ QTimer::singleShot(0, this, SLOT(doTest()));
+ }
+
+private slots:
+ void doTest()
+ {
+ QApplicationPrivate *privateClass = static_cast<QApplicationPrivate*>(QObjectPrivate::get(qApp));
+
+ {
+ QDialog *win1 = new QDialog;
+
+ // Test with a lock active so that the refcount doesn't drop to zero during these tests, causing a quit.
+ // (until we exit the scope)
+ QEventLoopLocker locker;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ win1->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QDialog *win2 = new QDialog;
+
+ win2->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 3);
+
+ delete win1;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ delete win2;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ win1 = new QDialog;
+
+ win1->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ JobObject *job1 = new JobObject(this);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 3);
+
+ delete win1;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ delete job1;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ QWidget *w1 = new QWidget;
+
+ w1->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QWidget *w2 = new QMainWindow;
+
+ w2->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 3);
+
+ QWidget *w3 = new QWidget(0, Qt::Dialog);
+
+ w3->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 4);
+
+ delete w3;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 3);
+
+ delete w2;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QWidget *subWidget1 = new QWidget(w1, Qt::Window);
+
+ // Even though We create a new widget and show it,
+ // the ref count does not go up because it is a child of
+ // w1, which is the top-level, and what we are actually
+ // refcounting.
+ subWidget1->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ // When we use setParent(0) and re-show, the
+ // ref count does increase:
+ QCOMPARE(subWidget1->isVisible(), true);
+ subWidget1->setParent(0);
+ QCOMPARE(subWidget1->isVisible(), false);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ subWidget1->show();
+ QCOMPARE(subWidget1->isVisible(), true);
+ QCOMPARE(privateClass->quitLockRef.load(), 3);
+
+ subWidget1->setParent(w1);
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QWidget *subWidget2 = new QWidget(w1);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ subWidget2->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ delete subWidget2;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QWidget *subWidget3 = new QWidget(w1);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ subWidget3->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ subWidget3->hide();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ delete subWidget3;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QWidget *subWidget4 = new QWidget(subWidget1);
+ QWidget *subWidget5 = new QWidget(subWidget1);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QWidget *subWidget6 = new QWidget(subWidget4, Qt::Window);
+
+ subWidget6->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ delete w1;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ w1 = new QWidget;
+ w2 = new QWidget;
+ w3 = new QWidget;
+
+ QHBoxLayout *layout = new QHBoxLayout(w1);
+
+ layout->addWidget(w2);
+ layout->addWidget(w3);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ w1->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ w1->hide();
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ delete w1;
+
+ }
+ QCOMPARE(privateClass->quitLockRef.load(), 0);
+ }
+};
+
+void tst_QApplication::testQuitLockRef()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qapplication" };
+ QApplication app(argc, argv);
+
+ QuitLockRefTester tester;
+
+ app.exec();
+}
+
+void tst_QApplication::testQuitLock1()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ QWidget *w = new QWidget;
+
+ w->show();
+
+ QMetaObject::invokeMethod(w, "close", Qt::QueuedConnection);
+
+ app.exec();
+
+ // No hang = pass.
+}
+
+void tst_QApplication::testQuitLock2()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ QWidget *w1 = new QWidget;
+
+ w1->show();
+
+ QWidget *w2 = new QWidget;
+
+ w2->show();
+
+ QMetaObject::invokeMethod(w1, "deleteLater", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(w2, "hide", Qt::QueuedConnection);
+
+ app.exec();
+
+ // No hang = pass.
+}
+
+class Result : public QObject
+{
+ Q_OBJECT
+public:
+ Result(QObject *parent = 0)
+ : QObject(parent), m_passes(false)
+ {
+
+ }
+
+ bool result() const
+ {
+ return m_passes;
+ }
+
+public slots:
+
+ void setPasses()
+ {
+ setResult(true);
+ }
+
+ void setFails()
+ {
+ setResult(false);
+ }
+
+ void setResult(bool result)
+ {
+ m_passes = result;
+ }
+
+private:
+ bool m_passes;
+};
+
+void tst_QApplication::testQuitLock3()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ Result *result = new Result(&app);
+
+ JobObject *job = new JobObject(&app);
+
+ QObject::connect(job, SIGNAL(done()), result, SLOT(setPasses()));
+
+ app.exec();
+
+ QVERIFY(result->result());
+}
+
+void tst_QApplication::testQuitLock4()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ QWidget *w = new QWidget;
+
+ w->show();
+
+ Result *result = new Result(&app);
+ JobObject *job = new JobObject(1000, &app);
+
+ QTimer::singleShot(500, w, SLOT(deleteLater()));
+
+ QObject::connect(w, SIGNAL(destroyed()), result, SLOT(setFails()));
+ QObject::connect(job, SIGNAL(done()), result, SLOT(setPasses()));
+
+ app.exec();
+
+ QVERIFY(result->result());
+}
+
+class JobBeforeWindowRunner : public QObject
+{
+ Q_OBJECT
+public:
+ JobBeforeWindowRunner(QObject *parent = 0)
+ : QObject(parent), m_result(new Result(this))
+ {
+
+ }
+
+ void start()
+ {
+ JobObject *job = new JobObject(this);
+ connect(job, SIGNAL(done()), m_result, SLOT(setFails()));
+ connect(job, SIGNAL(destroyed()), SLOT(showWindowDelayed()), Qt::QueuedConnection);
+ }
+
+ bool result() const { return m_result->result(); }
+
+private slots:
+ void showWindowDelayed()
+ {
+ qApp->setQuitLockEnabled(true);
+ QTimer::singleShot(500, this, SLOT(showWindow()));
+ }
+
+ void showWindow()
+ {
+ QWidget *w = new QWidget;
+ w->show();
+ w->deleteLater();
+ connect(w, SIGNAL(destroyed()), m_result, SLOT(setPasses()));
+ }
+
+private:
+ Result * const m_result;
+};
+
+void tst_QApplication::testQuitLock5()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+ app.setQuitLockEnabled(false);
+ // Run a job before showing a window, and only enable the refcounting
+ // after doing so.
+ // Although the job brings the refcount to zero, the app does not exit
+ // until setQuitLockEnabled is called and the feature re-enabled.
+
+ JobBeforeWindowRunner *eventRunner = new JobBeforeWindowRunner(&app);
+
+ eventRunner->start();
+
+ app.exec();
+
+ QVERIFY(eventRunner->result());
+}
+
+class JobDuringWindowRunner : public QObject
+{
+ Q_OBJECT
+public:
+ JobDuringWindowRunner(QObject *parent = 0)
+ : QObject(parent), m_result(new Result(this))
+ {
+
+ }
+
+ void start()
+ {
+ JobObject *job = new JobObject(this);
+
+ QWidget *w = new QWidget;
+ w->show();
+ w->deleteLater();
+
+ QObject::connect(w, SIGNAL(destroyed()), m_result, SLOT(setFails()));
+ QObject::connect(job, SIGNAL(done()), m_result, SLOT(setPasses()));
+ }
+
+ bool result() const { return m_result->result(); }
+
+private:
+ Result * const m_result;
+};
+
+void tst_QApplication::testQuitLock6()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ // A job runs, and while it is running, a window is shown and closed,
+ // then the job ends, which causes the quit.
+
+ JobDuringWindowRunner *eventRunner = new JobDuringWindowRunner(&app);
+
+ eventRunner->start();
+
+ app.exec();
+
+ QVERIFY(eventRunner->result());
+}
+class JobWindowJobWindowRunner : public QObject
+{
+ Q_OBJECT
+public:
+ JobWindowJobWindowRunner(QObject *parent = 0)
+ : QObject(parent), m_result(new Result(this))
+ {
+
+ }
+
+ void start()
+ {
+ JobObject *job = new JobObject(500, this);
+
+ QWidget *w = new QWidget;
+ w->show();
+ QTimer::singleShot(1000, w, SLOT(deleteLater()));
+
+ QObject::connect(w, SIGNAL(destroyed()), m_result, SLOT(setPasses()));
+ QObject::connect(job, SIGNAL(done()), m_result, SLOT(setFails()));
+ }
+
+ bool result() const { return m_result->result(); }
+private:
+ Result * const m_result;
+};
+
+void tst_QApplication::testQuitLock7()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ // A job runs, and while it is running, a window is shown
+ // then the job ends, then the window is closed, which causes the quit.
+
+ JobWindowJobWindowRunner *eventRunner = new JobWindowJobWindowRunner(&app);
+
+ eventRunner->start();
+
+ app.exec();
+
+ QVERIFY(eventRunner->result());
+}
+
+void tst_QApplication::testQuitLock8()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ QMainWindow *mw1 = new QMainWindow;
+ mw1->show();
+ QMainWindow *mw2 = new QMainWindow;
+ mw2->show();
+
+ QMetaObject::invokeMethod(mw1, "close", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(mw2, "close", Qt::QueuedConnection);
+
+ app.exec();
+
+ // No hang = pass
+}
+
+
/*
This test is meant to ensure that certain objects (public & commonly used)
can safely be used in a Q_GLOBAL_STATIC such that their destructors are