summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/global/qglobalstatic
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2012-02-24 18:44:17 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-01-22 17:15:58 +0100
commitf7eff7251786186ca895c8fc537b304bc89977b3 (patch)
tree161dd1a09188b8d3b61a5fd2dafbf43764d8424e /tests/auto/corelib/global/qglobalstatic
parent326a5e0bca9e77ce9d6a93e301c6b52a56278453 (diff)
Add a new Q_GLOBAL_STATIC implementation
Unlike the previous implementation, this implementation is locked: only one initialisation is ever run at the same time. It is exception-safe, meaning that a throwing constructor will restart the process. Also, start using the thread-safe behaviour that GCC has offered for a long time and C++11 requires. Change-Id: I20db44f57d258923df64c0051358fd0d9a5ccd51 Reviewed-by: Olivier Goffart <ogoffart@woboq.com> Reviewed-by: David Faure (KDE) <faure@kde.org> Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@digia.com>
Diffstat (limited to 'tests/auto/corelib/global/qglobalstatic')
-rw-r--r--tests/auto/corelib/global/qglobalstatic/qglobalstatic.pro12
-rw-r--r--tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp214
2 files changed, 226 insertions, 0 deletions
diff --git a/tests/auto/corelib/global/qglobalstatic/qglobalstatic.pro b/tests/auto/corelib/global/qglobalstatic/qglobalstatic.pro
new file mode 100644
index 0000000000..21cab8f67d
--- /dev/null
+++ b/tests/auto/corelib/global/qglobalstatic/qglobalstatic.pro
@@ -0,0 +1,12 @@
+QT += testlib core-private
+
+QT -= gui
+
+TARGET = tst_qglobalstatic
+CONFIG += console
+CONFIG -= app_bundle
+CONFIG += exceptions
+
+SOURCES += tst_qglobalstatic.cpp
+DEFINES += SRCDIR=\\\"$$PWD/\\\"
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp b/tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp
new file mode 100644
index 0000000000..b9aa70fe80
--- /dev/null
+++ b/tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp
@@ -0,0 +1,214 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Thiago Macieira <thiago@kde.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QThread>
+#include <QtTest/QtTest>
+
+class tst_QGlobalStatic : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void beforeInitialization();
+ void api();
+ void constVolatile();
+ void exception();
+ void threadStressTest();
+ void afterDestruction();
+};
+
+Q_GLOBAL_STATIC_WITH_ARGS(const int, constInt, (42))
+Q_GLOBAL_STATIC_WITH_ARGS(volatile int, volatileInt, (-47))
+
+void otherFunction()
+{
+ // never called
+ constInt();
+ volatileInt();
+}
+
+// do not initialize the following Q_GLOBAL_STATIC
+Q_GLOBAL_STATIC(int, checkedBeforeInitialization)
+void tst_QGlobalStatic::beforeInitialization()
+{
+ QVERIFY(!checkedBeforeInitialization.exists());
+ QVERIFY(!checkedBeforeInitialization.isDestroyed());
+}
+
+struct Type {
+ int i;
+};
+
+Q_GLOBAL_STATIC(Type, checkedAfterInitialization)
+void tst_QGlobalStatic::api()
+{
+ // check the API
+ QVERIFY((Type *)checkedAfterInitialization);
+ QVERIFY(checkedAfterInitialization());
+ *checkedAfterInitialization = Type();
+ *checkedAfterInitialization() = Type();
+
+ checkedAfterInitialization()->i = 47;
+ checkedAfterInitialization->i = 42;
+ QCOMPARE(checkedAfterInitialization()->i, 42);
+ checkedAfterInitialization()->i = 47;
+ QCOMPARE(checkedAfterInitialization->i, 47);
+
+ QVERIFY(checkedAfterInitialization.exists());
+ QVERIFY(!checkedAfterInitialization.isDestroyed());
+}
+
+void tst_QGlobalStatic::constVolatile()
+{
+ QCOMPARE(*constInt(), 42);
+ QCOMPARE((int)*volatileInt(), -47);
+ QCOMPARE(*constInt(), 42);
+ QCOMPARE((int)*volatileInt(), -47);
+}
+
+struct ThrowingType
+{
+ static QBasicAtomicInt constructedCount;
+ static QBasicAtomicInt destructedCount;
+ ThrowingType(QBasicAtomicInt &throwControl)
+ {
+ constructedCount.ref();
+ if (throwControl.fetchAndAddRelaxed(-1) != 0)
+ throw 0;
+ }
+ ~ThrowingType() { destructedCount.ref(); }
+};
+QBasicAtomicInt ThrowingType::constructedCount = Q_BASIC_ATOMIC_INITIALIZER(0);
+QBasicAtomicInt ThrowingType::destructedCount = Q_BASIC_ATOMIC_INITIALIZER(0);
+
+QBasicAtomicInt exceptionControlVar = Q_BASIC_ATOMIC_INITIALIZER(1);
+Q_GLOBAL_STATIC_WITH_ARGS(ThrowingType, exceptionGS, (exceptionControlVar))
+void tst_QGlobalStatic::exception()
+{
+ if (exceptionControlVar.load() != 1)
+ QSKIP("This test cannot be run more than once");
+ ThrowingType::constructedCount.store(0);
+ ThrowingType::destructedCount.store(0);
+
+ bool exceptionCaught = false;
+ try {
+ exceptionGS();
+ } catch (int) {
+ exceptionCaught = true;
+ }
+ QCOMPARE(ThrowingType::constructedCount.load(), 1);
+ QVERIFY(exceptionCaught);
+
+ exceptionGS();
+ QCOMPARE(ThrowingType::constructedCount.load(), 2);
+}
+
+QBasicAtomicInt threadStressTestControlVar = Q_BASIC_ATOMIC_INITIALIZER(5);
+Q_GLOBAL_STATIC_WITH_ARGS(ThrowingType, threadStressTestGS, (threadStressTestControlVar))
+
+
+void tst_QGlobalStatic::threadStressTest()
+{
+ class ThreadStressTestThread: public QThread
+ {
+ public:
+ QReadWriteLock *lock;
+ void run()
+ {
+ QReadLocker l(lock);
+ //usleep(qrand() * 200 / RAND_MAX);
+ // thundering herd
+ try {
+ threadStressTestGS();
+ } catch (int) {
+ }
+ }
+ };
+
+ ThrowingType::constructedCount.store(0);
+ ThrowingType::destructedCount.store(0);
+ int expectedConstructionCount = threadStressTestControlVar.load() + 1;
+ if (expectedConstructionCount <= 0)
+ QSKIP("This test cannot be run more than once");
+
+ const int numThreads = 200;
+ ThreadStressTestThread threads[numThreads];
+ QReadWriteLock lock;
+ lock.lockForWrite();
+ for (int i = 0; i < numThreads; ++i) {
+ threads[i].lock = &lock;
+ threads[i].start();
+ }
+
+ // wait for all threads
+ // release the herd
+ lock.unlock();
+
+ for (int i = 0; i < numThreads; ++i)
+ threads[i].wait();
+
+ QCOMPARE(ThrowingType::constructedCount.loadAcquire(), expectedConstructionCount);
+ QCOMPARE(ThrowingType::destructedCount.loadAcquire(), 0);
+}
+
+Q_GLOBAL_STATIC(int, checkedAfterDestruction)
+void tst_QGlobalStatic::afterDestruction()
+{
+ // this test will not produce results now
+ // it will simply run some code on destruction (after the global statics have been deleted)
+ // if that fails, this will cause a crash
+
+ // static destruction is LIFO: so we must add our exit-time code before the
+ // global static is used for the first time
+ static struct RunAtExit {
+ ~RunAtExit() {
+ int *ptr = checkedAfterDestruction();
+ if (ptr)
+ qFatal("Global static is not null as was expected");
+ }
+ } runAtExit;
+ (void) runAtExit;
+
+ *checkedAfterDestruction = 42;
+}
+
+QTEST_APPLESS_MAIN(tst_QGlobalStatic);
+
+#include "tst_qglobalstatic.moc"