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 --- tests/auto/corelib/thread/qthreadonce/.gitignore | 1 + .../corelib/thread/qthreadonce/qthreadonce.cpp | 121 +++++++++++ .../auto/corelib/thread/qthreadonce/qthreadonce.h | 114 ++++++++++ .../corelib/thread/qthreadonce/qthreadonce.pro | 13 ++ .../corelib/thread/qthreadonce/tst_qthreadonce.cpp | 234 +++++++++++++++++++++ 5 files changed, 483 insertions(+) create mode 100644 tests/auto/corelib/thread/qthreadonce/.gitignore create mode 100644 tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp create mode 100644 tests/auto/corelib/thread/qthreadonce/qthreadonce.h create mode 100644 tests/auto/corelib/thread/qthreadonce/qthreadonce.pro create mode 100644 tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp (limited to 'tests/auto/corelib/thread/qthreadonce') diff --git a/tests/auto/corelib/thread/qthreadonce/.gitignore b/tests/auto/corelib/thread/qthreadonce/.gitignore new file mode 100644 index 0000000000..856177d615 --- /dev/null +++ b/tests/auto/corelib/thread/qthreadonce/.gitignore @@ -0,0 +1 @@ +tst_qthreadonce diff --git a/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp b/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp new file mode 100644 index 0000000000..b23e11b153 --- /dev/null +++ b/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** 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 "qplatformdefs.h" +#include "qthreadonce.h" + +#ifndef QT_NO_THREAD +#include "qmutex.h" + +Q_GLOBAL_STATIC_WITH_ARGS(QMutex, onceInitializationMutex, (QMutex::Recursive)) + +enum QOnceExtra { + MustRunCode = 0x01, + MustUnlockMutex = 0x02 +}; + +/*! + \internal + Initialize the Q_ONCE structure. + + Q_ONCE consists of two variables: + - a static POD QOnceControl::ControlVariable (it's a QBasicAtomicInt) + - an automatic QOnceControl that controls the former + + The POD is initialized to 0. + + When QOnceControl's constructor starts, it'll lock the global + initialization mutex. It'll then check if it's the first to up + the control variable and will take note. + + The QOnceControl's destructor will unlock the global + initialization mutex. +*/ +QOnceControl::QOnceControl(QBasicAtomicInt *control) +{ + d = 0; + gv = control; + // check if code has already run once + if (*gv == 2) { + // uncontended case: it has already initialized + // no waiting + return; + } + + // acquire the path + onceInitializationMutex()->lock(); + extra = MustUnlockMutex; + + if (gv->testAndSetAcquire(0, 1)) { + // path acquired, we're the first + extra |= MustRunCode; + } +} + +QOnceControl::~QOnceControl() +{ + if (mustRunCode()) + // code wasn't run! + gv->testAndSetRelease(1, 0); + else + gv->testAndSetRelease(1, 2); + if (extra & MustUnlockMutex) + onceInitializationMutex()->unlock(); +} + +/*! + \internal + Returns true if the initialization code must be run. + + Obviously, the initialization code must be run only once... +*/ +bool QOnceControl::mustRunCode() +{ + return extra & MustRunCode; +} + +void QOnceControl::done() +{ + extra &= ~MustRunCode; +} + +#endif // QT_NO_THREAD diff --git a/tests/auto/corelib/thread/qthreadonce/qthreadonce.h b/tests/auto/corelib/thread/qthreadonce/qthreadonce.h new file mode 100644 index 0000000000..c33625cbde --- /dev/null +++ b/tests/auto/corelib/thread/qthreadonce/qthreadonce.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + + +#ifndef QTHREADONCE_H +#define QTHREADONCE_H + +#include +#include + +QT_BEGIN_HEADER + +QT_MODULE(Core) + +#ifndef QT_NO_THREAD + +class QOnceControl +{ +public: + QOnceControl(QBasicAtomicInt *); + ~QOnceControl(); + + bool mustRunCode(); + void done(); + +private: + QBasicAtomicInt *gv; + union { + qint32 extra; + void *d; + }; +}; + +#define Q_ONCE_GV_NAME2(prefix, line) prefix ## line +#define Q_ONCE_GV_NAME(prefix, line) Q_ONCE_GV_NAME2(prefix, line) +#define Q_ONCE_GV Q_ONCE_GV_NAME(_q_once_, __LINE__) + +#define Q_ONCE \ + static QBasicAtomicInt Q_ONCE_GV = Q_BASIC_ATOMIC_INITIALIZER(0); \ + if (0){} else \ + for (QOnceControl _control_(&Q_ONCE_GV); _control_.mustRunCode(); _control_.done()) + +template +class QSingleton +{ + // this is a POD-like class + struct Destructor + { + T *&pointer; + Destructor(T *&ptr) : pointer(ptr) {} + ~Destructor() { delete pointer; } + }; + +public: + T *_q_value; + QBasicAtomicInt _q_guard; + + inline T *value() + { + for (QOnceControl control(&_q_guard); control.mustRunCode(); control.done()) { + _q_value = new T(); + static Destructor cleanup(_q_value); + } + return _q_value; + } + + inline T& operator*() { return *value(); } + inline T* operator->() { return value(); } + inline operator T*() { return value(); } +}; + +#endif // QT_NO_THREAD + +QT_END_HEADER + +#endif diff --git a/tests/auto/corelib/thread/qthreadonce/qthreadonce.pro b/tests/auto/corelib/thread/qthreadonce/qthreadonce.pro new file mode 100644 index 0000000000..d7ef4d4c23 --- /dev/null +++ b/tests/auto/corelib/thread/qthreadonce/qthreadonce.pro @@ -0,0 +1,13 @@ +load(qttest_p4) +SOURCES += tst_qthreadonce.cpp +QT = core + +# Don't use gcc's threadsafe statics +# Note: some versions of gcc generate invalid code with this option... +# Some versions of gcc don't even have it, so disable it +#*-g++*:QMAKE_CXXFLAGS += -fno-threadsafe-statics + +# Temporary: +SOURCES += qthreadonce.cpp + +CONFIG += parallel_test diff --git a/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp b/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp new file mode 100644 index 0000000000..dea4e43fef --- /dev/null +++ b/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** 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 "qthreadonce.h" + +//TESTED_CLASS= +//TESTED_FILES=corelib/thread/qthreadonce.h corelib/thread/qthreadonce.cpp + +class tst_QThreadOnce : public QObject +{ + Q_OBJECT + +private slots: + void sameThread(); + void sameThread_data(); + void multipleThreads(); + + void nesting(); + void reentering(); + void exception(); +}; + +class SingletonObject: public QObject +{ + Q_OBJECT +public: + static int runCount; + SingletonObject() { val = 42; ++runCount; } + ~SingletonObject() { } + + QBasicAtomicInt val; +}; + +class IncrementThread: public QThread +{ +public: + static QBasicAtomicInt runCount; + static QSingleton singleton; + QSemaphore &sem1, &sem2; + int &var; + + IncrementThread(QSemaphore *psem1, QSemaphore *psem2, int *pvar, QObject *parent) + : QThread(parent), sem1(*psem1), sem2(*psem2), var(*pvar) + { start(); } + + ~IncrementThread() { wait(); } + +protected: + void run() + { + sem2.release(); + sem1.acquire(); // synchronize + + Q_ONCE { + ++var; + } + runCount.ref(); + singleton->val.ref(); + } +}; +int SingletonObject::runCount = 0; +QBasicAtomicInt IncrementThread::runCount = Q_BASIC_ATOMIC_INITIALIZER(0); +QSingleton IncrementThread::singleton; + +void tst_QThreadOnce::sameThread_data() +{ + SingletonObject::runCount = 0; + QTest::addColumn("expectedValue"); + + QTest::newRow("first") << 42; + QTest::newRow("second") << 43; +} + +void tst_QThreadOnce::sameThread() +{ + static int controlVariable = 0; + Q_ONCE { + QCOMPARE(controlVariable, 0); + ++controlVariable; + } + QCOMPARE(controlVariable, 1); + + static QSingleton s; + QTEST((int)s->val, "expectedValue"); + s->val.ref(); + + QCOMPARE(SingletonObject::runCount, 1); +} + +void tst_QThreadOnce::multipleThreads() +{ +#if defined(Q_OS_WINCE) || defined(Q_OS_VXWORKS) || defined(Q_OS_SYMBIAN) + const int NumberOfThreads = 20; +#else + const int NumberOfThreads = 100; +#endif + int controlVariable = 0; + QSemaphore sem1, sem2(NumberOfThreads); + + QObject *parent = new QObject; + for (int i = 0; i < NumberOfThreads; ++i) + new IncrementThread(&sem1, &sem2, &controlVariable, parent); + + QCOMPARE(controlVariable, 0); // nothing must have set them yet + SingletonObject::runCount = 0; + IncrementThread::runCount = 0; + + // wait for all of them to be ready + sem2.acquire(NumberOfThreads); + // unleash the threads + sem1.release(NumberOfThreads); + + // wait for all of them to terminate: + delete parent; + + QCOMPARE(controlVariable, 1); + QCOMPARE((int)IncrementThread::runCount, NumberOfThreads); + QCOMPARE(SingletonObject::runCount, 1); +} + +void tst_QThreadOnce::nesting() +{ + int variable = 0; + Q_ONCE { + Q_ONCE { + ++variable; + } + } + + QVERIFY(variable == 1); +} + +static void reentrant(int control, int &counter) +{ + Q_ONCE { + if (counter) + reentrant(--control, counter); + ++counter; + } + static QSingleton s; + s->val.ref(); +} + +void tst_QThreadOnce::reentering() +{ + const int WantedRecursions = 5; + int count = 0; + SingletonObject::runCount = 0; + reentrant(WantedRecursions, count); + + // reentrancy is undefined behavior: + QVERIFY(count == 1 || count == WantedRecursions); + QCOMPARE(SingletonObject::runCount, 1); +} + +#if !defined(QT_NO_EXCEPTIONS) +static void exception_helper(int &val) +{ + Q_ONCE { + if (val++ == 0) throw 0; + } +} +#endif + +void tst_QThreadOnce::exception() +{ +#if defined(QT_NO_EXCEPTIONS) + QSKIP("Compiled without exceptions, skipping test", SkipAll); +#else + int count = 0; + + try { + exception_helper(count); + } catch (...) { + // nothing + } + QCOMPARE(count, 1); + + try { + exception_helper(count); + } catch (...) { + QVERIFY2(false, "Exception shouldn't have been thrown..."); + } + QCOMPARE(count, 2); +#endif +} + +QTEST_MAIN(tst_QThreadOnce) +#include "tst_qthreadonce.moc" -- cgit v1.2.3