From 82e715b2770258fa2c536aeae1f46c7fccdcdecf Mon Sep 17 00:00:00 2001 From: Holger Ihrig Date: Fri, 26 Aug 2011 12:56:14 +0200 Subject: Moving relevant tests to corelib/thread Task-number: QTBUG-21066 Change-Id: Ia16fa8961f1a73f4da6709197b5dd9929c16583f Reviewed-on: http://codereview.qt.nokia.com/3663 Reviewed-by: Qt Sanity Bot Reviewed-by: Rohan McGovern Reviewed-by: Jason McDonald --- .../auto/corelib/thread/qthreadstorage/.gitignore | 1 + .../corelib/thread/qthreadstorage/crashOnExit.cpp | 64 +++ .../corelib/thread/qthreadstorage/crashOnExit.pro | 4 + .../thread/qthreadstorage/qthreadstorage.pro | 5 + .../thread/qthreadstorage/tst_qthreadstorage.cpp | 503 +++++++++++++++++++++ .../thread/qthreadstorage/tst_qthreadstorage.pro | 4 + 6 files changed, 581 insertions(+) create mode 100644 tests/auto/corelib/thread/qthreadstorage/.gitignore create mode 100644 tests/auto/corelib/thread/qthreadstorage/crashOnExit.cpp create mode 100644 tests/auto/corelib/thread/qthreadstorage/crashOnExit.pro create mode 100644 tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro create mode 100644 tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp create mode 100644 tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.pro (limited to 'tests/auto/corelib/thread/qthreadstorage') diff --git a/tests/auto/corelib/thread/qthreadstorage/.gitignore b/tests/auto/corelib/thread/qthreadstorage/.gitignore new file mode 100644 index 0000000000..301968a8f6 --- /dev/null +++ b/tests/auto/corelib/thread/qthreadstorage/.gitignore @@ -0,0 +1 @@ +tst_qthreadstorage diff --git a/tests/auto/corelib/thread/qthreadstorage/crashOnExit.cpp b/tests/auto/corelib/thread/qthreadstorage/crashOnExit.cpp new file mode 100644 index 0000000000..92efcf3b3d --- /dev/null +++ b/tests/auto/corelib/thread/qthreadstorage/crashOnExit.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +#include + +class Class +{ +public: + ~Class() + { + // trigger creation of a new QThreadStorage, after the previous QThreadStorage from main() was destructed + static QThreadStorage threadstorage; + threadstorage.setLocalData(new int); + threadstorage.setLocalData(new int); + } +}; + +int main() +{ + // instantiate the class that will use QThreadStorage from its destructor, it's destructor will be run last + static Class instance; + // instantiate QThreadStorage, it's destructor (and the global destructors for QThreadStorages internals) will run first + static QThreadStorage threadstorage; + threadstorage.setLocalData(new int); + threadstorage.setLocalData(new int); +} diff --git a/tests/auto/corelib/thread/qthreadstorage/crashOnExit.pro b/tests/auto/corelib/thread/qthreadstorage/crashOnExit.pro new file mode 100644 index 0000000000..918ef398ba --- /dev/null +++ b/tests/auto/corelib/thread/qthreadstorage/crashOnExit.pro @@ -0,0 +1,4 @@ +SOURCES += crashOnExit.cpp +QT = core +CONFIG-=app_bundle +CONFIG+=console diff --git a/tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro b/tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro new file mode 100644 index 0000000000..0dc8d086df --- /dev/null +++ b/tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs +SUBDIRS = \ + tst_qthreadstorage.pro \ + crashOnExit.pro +CONFIG += parallel_test diff --git a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp new file mode 100644 index 0000000000..e123fc4fea --- /dev/null +++ b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp @@ -0,0 +1,503 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + + +#include + +#include +#include +#include +#include +#include + +#ifdef Q_OS_UNIX +#include +#endif +#ifdef Q_OS_WIN +#ifndef Q_OS_WINCE +#include +#endif +#include +#endif + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QThreadStorage : public QObject +{ + Q_OBJECT + +public: + tst_QThreadStorage(); + +private slots: + void hasLocalData(); + void localData(); + void localData_const(); + void setLocalData(); + void autoDelete(); + void adoptedThreads(); + void ensureCleanupOrder(); + void QTBUG13877_crashOnExit(); + void QTBUG14579_leakInDestructor(); + void QTBUG14579_resetInDestructor(); + void valueBased(); +}; + +class Pointer +{ +public: + static int count; + inline Pointer() { ++count; } + inline ~Pointer() { --count; } +}; +int Pointer::count = 0; + +tst_QThreadStorage::tst_QThreadStorage() + +{ } + +void tst_QThreadStorage::hasLocalData() +{ + QThreadStorage pointers; + QVERIFY(!pointers.hasLocalData()); + pointers.setLocalData(new Pointer); + QVERIFY(pointers.hasLocalData()); + pointers.setLocalData(0); + QVERIFY(!pointers.hasLocalData()); +} + +void tst_QThreadStorage::localData() +{ + QThreadStorage pointers; + Pointer *p = new Pointer; + QVERIFY(!pointers.hasLocalData()); + pointers.setLocalData(p); + QVERIFY(pointers.hasLocalData()); + QCOMPARE(pointers.localData(), p); + pointers.setLocalData(0); + QCOMPARE(pointers.localData(), (Pointer *)0); + QVERIFY(!pointers.hasLocalData()); +} + +void tst_QThreadStorage::localData_const() +{ + QThreadStorage pointers; + const QThreadStorage &const_pointers = pointers; + Pointer *p = new Pointer; + QVERIFY(!pointers.hasLocalData()); + pointers.setLocalData(p); + QVERIFY(pointers.hasLocalData()); + QCOMPARE(const_pointers.localData(), p); + pointers.setLocalData(0); + QCOMPARE(const_pointers.localData(), (Pointer *)0); + QVERIFY(!pointers.hasLocalData()); +} + +void tst_QThreadStorage::setLocalData() +{ + QThreadStorage pointers; + QVERIFY(!pointers.hasLocalData()); + pointers.setLocalData(new Pointer); + QVERIFY(pointers.hasLocalData()); + pointers.setLocalData(0); + QVERIFY(!pointers.hasLocalData()); +} + +class Thread : public QThread +{ +public: + QThreadStorage &pointers; + + QMutex mutex; + QWaitCondition cond; + + Thread(QThreadStorage &p) + : pointers(p) + { } + + void run() + { + pointers.setLocalData(new Pointer); + + QMutexLocker locker(&mutex); + cond.wakeOne(); + cond.wait(&mutex); + } +}; + +void tst_QThreadStorage::autoDelete() +{ + QThreadStorage pointers; + QVERIFY(!pointers.hasLocalData()); + + Thread thread(pointers); + int c = Pointer::count; + { + QMutexLocker locker(&thread.mutex); + thread.start(); + thread.cond.wait(&thread.mutex); + // QCOMPARE(Pointer::count, c + 1); + thread.cond.wakeOne(); + } + thread.wait(); + QCOMPARE(Pointer::count, c); +} + +bool threadStorageOk; +void testAdoptedThreadStorageWin(void *p) +{ + QThreadStorage *pointers = reinterpret_cast *>(p); + if (pointers->hasLocalData()) { + threadStorageOk = false; + return; + } + + Pointer *pointer = new Pointer(); + pointers->setLocalData(pointer); + + if (pointers->hasLocalData() == false) { + threadStorageOk = false; + return; + } + + if (pointers->localData() != pointer) { + threadStorageOk = false; + return; + } + QObject::connect(QThread::currentThread(), SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); +} +void *testAdoptedThreadStorageUnix(void *pointers) +{ + testAdoptedThreadStorageWin(pointers); + return 0; +} +void tst_QThreadStorage::adoptedThreads() +{ + QTestEventLoop::instance(); // Make sure the instance is created in this thread. + QThreadStorage pointers; + int c = Pointer::count; + threadStorageOk = true; + { +#ifdef Q_OS_UNIX + pthread_t thread; + const int state = pthread_create(&thread, 0, testAdoptedThreadStorageUnix, &pointers); + QCOMPARE(state, 0); + pthread_join(thread, 0); +#elif defined Q_OS_WIN + HANDLE thread; +#if defined(Q_OS_WINCE) + thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)testAdoptedThreadStorageWin, &pointers, 0, NULL); +#else + thread = (HANDLE)_beginthread(testAdoptedThreadStorageWin, 0, &pointers); +#endif + QVERIFY(thread); + WaitForSingleObject(thread, INFINITE); +#endif + } + QVERIFY(threadStorageOk); + + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(Pointer::count, c); +} + +QBasicAtomicInt cleanupOrder = Q_BASIC_ATOMIC_INITIALIZER(0); + +class First +{ +public: + ~First() + { + order = cleanupOrder.fetchAndAddRelaxed(1); + } + static int order; +}; +int First::order = -1; + +class Second +{ +public: + ~Second() + { + order = cleanupOrder.fetchAndAddRelaxed(1); + } + static int order; +}; +int Second::order = -1; + +void tst_QThreadStorage::ensureCleanupOrder() +{ + class Thread : public QThread + { + public: + QThreadStorage &first; + QThreadStorage &second; + + Thread(QThreadStorage &first, + QThreadStorage &second) + : first(first), second(second) + { } + + void run() + { + // set in reverse order, but shouldn't matter, the data + // will be deleted in the order the thread storage objects + // were created + second.setLocalData(new Second); + first.setLocalData(new First); + } + }; + + QThreadStorage second; + QThreadStorage first; + Thread thread(first, second); + thread.start(); + thread.wait(); + + QVERIFY(First::order < Second::order); +} + +void tst_QThreadStorage::QTBUG13877_crashOnExit() +{ + QProcess process; +#ifdef Q_OS_WIN +# ifdef QT_NO_DEBUG + process.start("release/crashOnExit"); +# else + process.start("debug/crashOnExit"); +# endif +#else + process.start("./crashOnExit"); +#endif + QVERIFY(process.waitForFinished()); + QVERIFY(process.exitStatus() != QProcess::CrashExit); +} + +// S stands for thread Safe. +class SPointer +{ +public: + static QBasicAtomicInt count; + inline SPointer() { count.ref(); } + inline ~SPointer() { count.deref(); } + inline SPointer(const SPointer & /* other */) { count.ref(); } +}; +QBasicAtomicInt SPointer::count = Q_BASIC_ATOMIC_INITIALIZER(0); + +Q_GLOBAL_STATIC(QThreadStorage, QTBUG14579_pointers1) +Q_GLOBAL_STATIC(QThreadStorage, QTBUG14579_pointers2) + +class QTBUG14579_class +{ +public: + SPointer member; + inline ~QTBUG14579_class() { + QVERIFY(!QTBUG14579_pointers1()->hasLocalData()); + QVERIFY(!QTBUG14579_pointers2()->hasLocalData()); + QTBUG14579_pointers2()->setLocalData(new SPointer); + QTBUG14579_pointers1()->setLocalData(new SPointer); + QVERIFY(QTBUG14579_pointers1()->hasLocalData()); + QVERIFY(QTBUG14579_pointers2()->hasLocalData()); + } +}; + + +void tst_QThreadStorage::QTBUG14579_leakInDestructor() +{ + class Thread : public QThread + { + public: + QThreadStorage &tls; + + Thread(QThreadStorage &t) : tls(t) { } + + void run() + { + QVERIFY(!tls.hasLocalData()); + tls.setLocalData(new QTBUG14579_class); + QVERIFY(tls.hasLocalData()); + } + }; + int c = SPointer::count; + + QThreadStorage tls; + + QVERIFY(!QTBUG14579_pointers1()->hasLocalData()); + QThreadStorage tls2; //add some more tls to make sure ids are not following each other too much + QThreadStorage tls3; + QVERIFY(!tls2.hasLocalData()); + QVERIFY(!tls3.hasLocalData()); + QVERIFY(!tls.hasLocalData()); + + Thread t1(tls); + Thread t2(tls); + Thread t3(tls); + + t1.start(); + t2.start(); + t3.start(); + + QVERIFY(t1.wait()); + QVERIFY(t2.wait()); + QVERIFY(t3.wait()); + + //check all the constructed things have been destructed + QCOMPARE(int(SPointer::count), c); +} + +class QTBUG14579_reset { +public: + SPointer member; + ~QTBUG14579_reset(); +}; + +Q_GLOBAL_STATIC(QThreadStorage, QTBUG14579_resetTls) + +QTBUG14579_reset::~QTBUG14579_reset() { + //Quite stupid, but WTF::ThreadSpecific::destroy does it. + QTBUG14579_resetTls()->setLocalData(this); +} + +void tst_QThreadStorage::QTBUG14579_resetInDestructor() +{ + class Thread : public QThread + { + public: + void run() + { + QVERIFY(!QTBUG14579_resetTls()->hasLocalData()); + QTBUG14579_resetTls()->setLocalData(new QTBUG14579_reset); + QVERIFY(QTBUG14579_resetTls()->hasLocalData()); + } + }; + int c = SPointer::count; + + Thread t1; + Thread t2; + Thread t3; + t1.start(); + t2.start(); + t3.start(); + QVERIFY(t1.wait()); + QVERIFY(t2.wait()); + QVERIFY(t3.wait()); + + //check all the constructed things have been destructed + QCOMPARE(int(SPointer::count), c); +} + + +void tst_QThreadStorage::valueBased() +{ + struct Thread : QThread { + QThreadStorage &tlsSPointer; + QThreadStorage &tlsString; + QThreadStorage &tlsInt; + + int someNumber; + QString someString; + Thread(QThreadStorage &t1, QThreadStorage &t2, QThreadStorage &t3) + : tlsSPointer(t1), tlsString(t2), tlsInt(t3) { } + + void run() { + /*QVERIFY(!tlsSPointer.hasLocalData()); + QVERIFY(!tlsString.hasLocalData()); + QVERIFY(!tlsInt.hasLocalData());*/ + SPointer pointercopy = tlsSPointer.localData(); + + //Default constructed values + QVERIFY(tlsString.localData().isNull()); + QCOMPARE(tlsInt.localData(), 0); + + //setting + tlsString.setLocalData(someString); + tlsInt.setLocalData(someNumber); + + QCOMPARE(tlsString.localData(), someString); + QCOMPARE(tlsInt.localData(), someNumber); + + //changing + tlsSPointer.setLocalData(SPointer()); + tlsInt.localData() += 42; + tlsString.localData().append(QLatin1String(" world")); + + QCOMPARE(tlsString.localData(), (someString + QLatin1String(" world"))); + QCOMPARE(tlsInt.localData(), (someNumber + 42)); + + // operator= + tlsString.localData() = QString::number(someNumber); + QCOMPARE(tlsString.localData().toInt(), someNumber); + } + }; + + QThreadStorage tlsSPointer; + QThreadStorage tlsString; + QThreadStorage tlsInt; + + int c = SPointer::count; + + Thread t1(tlsSPointer, tlsString, tlsInt); + Thread t2(tlsSPointer, tlsString, tlsInt); + Thread t3(tlsSPointer, tlsString, tlsInt); + t1.someNumber = 42; + t2.someNumber = -128; + t3.someNumber = 78; + t1.someString = "hello"; + t2.someString = "trolltech"; + t3.someString = "nokia"; + + t1.start(); + t2.start(); + t3.start(); + + QVERIFY(t1.wait()); + QVERIFY(t2.wait()); + QVERIFY(t3.wait()); + + QCOMPARE(c, int(SPointer::count)); + +} + + +QTEST_MAIN(tst_QThreadStorage) +#include "tst_qthreadstorage.moc" diff --git a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.pro b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.pro new file mode 100644 index 0000000000..3071098629 --- /dev/null +++ b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +SOURCES += tst_qthreadstorage.cpp +QT = core +symbian:LIBS += -llibpthread -- cgit v1.2.3