From f7eff7251786186ca895c8fc537b304bc89977b3 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 24 Feb 2012 18:44:17 +0100 Subject: Add a new Q_GLOBAL_STATIC implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: David Faure (KDE) Reviewed-by: Jędrzej Nowacki --- .../corelib/global/qglobalstatic/qglobalstatic.pro | 12 ++ .../global/qglobalstatic/tst_qglobalstatic.cpp | 214 +++++++++++++++++++++ tests/auto/corelib/kernel/qobject/tst_qobject.cpp | 10 + 3 files changed, 236 insertions(+) create mode 100644 tests/auto/corelib/global/qglobalstatic/qglobalstatic.pro create mode 100644 tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp (limited to 'tests/auto/corelib') 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 +** 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 +#include + +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" diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 581644b19f..d7add383e1 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -144,6 +144,16 @@ private slots: void disconnectDoesNotLeakFunctor(); }; +struct QObjectCreatedOnShutdown +{ + QObjectCreatedOnShutdown() {} + ~QObjectCreatedOnShutdown() + { + QObject(); + } +}; +static QObjectCreatedOnShutdown s_qobjectCreatedOnShutdown; + class SenderObject : public QObject { Q_OBJECT -- cgit v1.2.3