diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /tests/auto/qthreadonce | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'tests/auto/qthreadonce')
-rw-r--r-- | tests/auto/qthreadonce/.gitignore | 1 | ||||
-rw-r--r-- | tests/auto/qthreadonce/qthreadonce.cpp | 121 | ||||
-rw-r--r-- | tests/auto/qthreadonce/qthreadonce.h | 114 | ||||
-rw-r--r-- | tests/auto/qthreadonce/qthreadonce.pro | 12 | ||||
-rw-r--r-- | tests/auto/qthreadonce/tst_qthreadonce.cpp | 234 |
5 files changed, 482 insertions, 0 deletions
diff --git a/tests/auto/qthreadonce/.gitignore b/tests/auto/qthreadonce/.gitignore new file mode 100644 index 0000000..856177d --- /dev/null +++ b/tests/auto/qthreadonce/.gitignore @@ -0,0 +1 @@ +tst_qthreadonce diff --git a/tests/auto/qthreadonce/qthreadonce.cpp b/tests/auto/qthreadonce/qthreadonce.cpp new file mode 100644 index 0000000..1c7dc7e --- /dev/null +++ b/tests/auto/qthreadonce/qthreadonce.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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/qthreadonce/qthreadonce.h b/tests/auto/qthreadonce/qthreadonce.h new file mode 100644 index 0000000..d5f9cc9 --- /dev/null +++ b/tests/auto/qthreadonce/qthreadonce.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QTHREADONCE_H +#define QTHREADONCE_H + +#include <QtCore/qglobal.h> +#include <QtCore/qatomic.h> + +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<typename T> +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/qthreadonce/qthreadonce.pro b/tests/auto/qthreadonce/qthreadonce.pro new file mode 100644 index 0000000..a672a03 --- /dev/null +++ b/tests/auto/qthreadonce/qthreadonce.pro @@ -0,0 +1,12 @@ +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 + diff --git a/tests/auto/qthreadonce/tst_qthreadonce.cpp b/tests/auto/qthreadonce/tst_qthreadonce.cpp new file mode 100644 index 0000000..7e67dc3 --- /dev/null +++ b/tests/auto/qthreadonce/tst_qthreadonce.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <qcoreapplication.h> +#include <qmutex.h> +#include <qthread.h> +#include <qwaitcondition.h> +#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<SingletonObject> 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<SingletonObject> IncrementThread::singleton; + +void tst_QThreadOnce::sameThread_data() +{ + SingletonObject::runCount = 0; + QTest::addColumn<int>("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<SingletonObject> s; + QTEST((int)s->val, "expectedValue"); + s->val.ref(); + + QCOMPARE(SingletonObject::runCount, 1); +} + +void tst_QThreadOnce::multipleThreads() +{ +#ifdef Q_OS_WINCE + 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<SingletonObject> 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" |