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/qatomicint/.gitignore | 1 + .../auto/corelib/thread/qatomicint/qatomicint.pro | 4 + .../corelib/thread/qatomicint/tst_qatomicint.cpp | 791 +++++++++++++ .../auto/corelib/thread/qatomicpointer/.gitignore | 1 + .../thread/qatomicpointer/qatomicpointer.pro | 4 + .../thread/qatomicpointer/tst_qatomicpointer.cpp | 674 +++++++++++ tests/auto/corelib/thread/qmutex/.gitignore | 1 + tests/auto/corelib/thread/qmutex/qmutex.pro | 5 + tests/auto/corelib/thread/qmutex/tst_qmutex.cpp | 640 ++++++++++ tests/auto/corelib/thread/qmutexlocker/.gitignore | 1 + .../corelib/thread/qmutexlocker/qmutexlocker.pro | 4 + .../thread/qmutexlocker/tst_qmutexlocker.cpp | 236 ++++ tests/auto/corelib/thread/qreadlocker/.gitignore | 1 + .../corelib/thread/qreadlocker/qreadlocker.pro | 4 + .../corelib/thread/qreadlocker/tst_qreadlocker.cpp | 231 ++++ .../auto/corelib/thread/qreadwritelock/.gitignore | 1 + .../thread/qreadwritelock/qreadwritelock.pro | 4 + .../thread/qreadwritelock/tst_qreadwritelock.cpp | 1140 ++++++++++++++++++ tests/auto/corelib/thread/qsemaphore/.gitignore | 1 + .../auto/corelib/thread/qsemaphore/qsemaphore.pro | 7 + .../corelib/thread/qsemaphore/tst_qsemaphore.cpp | 450 +++++++ tests/auto/corelib/thread/qthread/.gitignore | 1 + tests/auto/corelib/thread/qthread/qthread.pro | 5 + tests/auto/corelib/thread/qthread/tst_qthread.cpp | 1249 ++++++++++++++++++++ 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 ++++ .../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 + .../auto/corelib/thread/qwaitcondition/.gitignore | 1 + .../thread/qwaitcondition/qwaitcondition.pro | 6 + .../thread/qwaitcondition/tst_qwaitcondition.cpp | 845 +++++++++++++ tests/auto/corelib/thread/qwritelocker/.gitignore | 1 + .../corelib/thread/qwritelocker/qwritelocker.pro | 4 + .../thread/qwritelocker/tst_qwritelocker.cpp | 231 ++++ tests/auto/corelib/thread/thread.pro | 14 + 42 files changed, 7622 insertions(+) create mode 100644 tests/auto/corelib/thread/qatomicint/.gitignore create mode 100644 tests/auto/corelib/thread/qatomicint/qatomicint.pro create mode 100644 tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp create mode 100644 tests/auto/corelib/thread/qatomicpointer/.gitignore create mode 100644 tests/auto/corelib/thread/qatomicpointer/qatomicpointer.pro create mode 100644 tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp create mode 100644 tests/auto/corelib/thread/qmutex/.gitignore create mode 100644 tests/auto/corelib/thread/qmutex/qmutex.pro create mode 100644 tests/auto/corelib/thread/qmutex/tst_qmutex.cpp create mode 100644 tests/auto/corelib/thread/qmutexlocker/.gitignore create mode 100644 tests/auto/corelib/thread/qmutexlocker/qmutexlocker.pro create mode 100644 tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp create mode 100644 tests/auto/corelib/thread/qreadlocker/.gitignore create mode 100644 tests/auto/corelib/thread/qreadlocker/qreadlocker.pro create mode 100644 tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp create mode 100644 tests/auto/corelib/thread/qreadwritelock/.gitignore create mode 100644 tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro create mode 100644 tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp create mode 100644 tests/auto/corelib/thread/qsemaphore/.gitignore create mode 100644 tests/auto/corelib/thread/qsemaphore/qsemaphore.pro create mode 100644 tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp create mode 100644 tests/auto/corelib/thread/qthread/.gitignore create mode 100644 tests/auto/corelib/thread/qthread/qthread.pro create mode 100644 tests/auto/corelib/thread/qthread/tst_qthread.cpp 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 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 create mode 100644 tests/auto/corelib/thread/qwaitcondition/.gitignore create mode 100644 tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro create mode 100644 tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp create mode 100644 tests/auto/corelib/thread/qwritelocker/.gitignore create mode 100644 tests/auto/corelib/thread/qwritelocker/qwritelocker.pro create mode 100644 tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp create mode 100644 tests/auto/corelib/thread/thread.pro (limited to 'tests/auto/corelib/thread') diff --git a/tests/auto/corelib/thread/qatomicint/.gitignore b/tests/auto/corelib/thread/qatomicint/.gitignore new file mode 100644 index 0000000000..52998efd02 --- /dev/null +++ b/tests/auto/corelib/thread/qatomicint/.gitignore @@ -0,0 +1 @@ +tst_qatomicint diff --git a/tests/auto/corelib/thread/qatomicint/qatomicint.pro b/tests/auto/corelib/thread/qatomicint/qatomicint.pro new file mode 100644 index 0000000000..7850d93dc7 --- /dev/null +++ b/tests/auto/corelib/thread/qatomicint/qatomicint.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +SOURCES += tst_qatomicint.cpp +QT = core +CONFIG += parallel_test diff --git a/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp new file mode 100644 index 0000000000..27a2cc2e9c --- /dev/null +++ b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp @@ -0,0 +1,791 @@ +/**************************************************************************** +** +** 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 + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QAtomicInt : public QObject +{ + Q_OBJECT + +public: + tst_QAtomicInt(); + ~tst_QAtomicInt(); + +private slots: + void warningFree(); + + // QAtomicInt members + void constructor_data(); + void constructor(); + void copy_constructor_data(); + void copy_constructor(); + void equality_operator_data(); + void equality_operator(); + void inequality_operator_data(); + void inequality_operator(); + void not_operator_data(); + void not_operator(); + void cast_operator_data(); + void cast_operator(); + void assignment_operator_data(); + void assignment_operator(); + + void isReferenceCountingNative(); + void isReferenceCountingWaitFree(); + void ref_data(); + void ref(); + void deref_data(); + void deref(); + + void isTestAndSetNative(); + void isTestAndSetWaitFree(); + void testAndSet_data(); + void testAndSet(); + + void isFetchAndStoreNative(); + void isFetchAndStoreWaitFree(); + void fetchAndStore_data(); + void fetchAndStore(); + + void isFetchAndAddNative(); + void isFetchAndAddWaitFree(); + void fetchAndAdd_data(); + void fetchAndAdd(); + + // stress tests + void testAndSet_loop(); + void fetchAndAdd_loop(); + void fetchAndAdd_threadedLoop(); + +private: + static void warningFreeHelper(); +}; + +tst_QAtomicInt::tst_QAtomicInt() +{ } + +tst_QAtomicInt::~tst_QAtomicInt() +{ } + +void tst_QAtomicInt::warningFreeHelper() +{ + qFatal("This code is bogus, and shouldn't be run. We're looking for compiler warnings only."); + + QBasicAtomicInt i = Q_BASIC_ATOMIC_INITIALIZER(0); + + int expectedValue = 0; + int newValue = 0; + int valueToAdd = 0; + + i.ref(); + i.deref(); + + i.testAndSetRelaxed(expectedValue, newValue); + i.testAndSetAcquire(expectedValue, newValue); + i.testAndSetRelease(expectedValue, newValue); + i.testAndSetOrdered(expectedValue, newValue); + + i.fetchAndStoreRelaxed(newValue); + i.fetchAndStoreAcquire(newValue); + i.fetchAndStoreRelease(newValue); + i.fetchAndStoreOrdered(newValue); + + i.fetchAndAddRelaxed(valueToAdd); + i.fetchAndAddAcquire(valueToAdd); + i.fetchAndAddRelease(valueToAdd); + i.fetchAndAddOrdered(valueToAdd); +} + +void tst_QAtomicInt::warningFree() +{ + // This is a compile time check for warnings. + // No need for actual work here. + + void (*foo)() = &warningFreeHelper; + (void)foo; +} + +void tst_QAtomicInt::constructor_data() +{ + QTest::addColumn("value"); + + QTest::newRow("0") << 31337; + QTest::newRow("1") << 0; + QTest::newRow("2") << 1; + QTest::newRow("3") << -1; + QTest::newRow("4") << 2; + QTest::newRow("5") << -2; + QTest::newRow("6") << 3; + QTest::newRow("7") << -3; + QTest::newRow("8") << INT_MAX; + QTest::newRow("9") << INT_MIN+1; +} + +void tst_QAtomicInt::constructor() +{ + QFETCH(int, value); + QAtomicInt atomic1(value); + QCOMPARE(int(atomic1), value); + QAtomicInt atomic2 = value; + QCOMPARE(int(atomic2), value); +} + +void tst_QAtomicInt::copy_constructor_data() +{ constructor_data(); } + +void tst_QAtomicInt::copy_constructor() +{ + QFETCH(int, value); + QAtomicInt atomic1(value); + QCOMPARE(int(atomic1), value); + + QAtomicInt atomic2(atomic1); + QCOMPARE(int(atomic2), value); + QAtomicInt atomic3 = atomic1; + QCOMPARE(int(atomic3), value); + QAtomicInt atomic4(atomic2); + QCOMPARE(int(atomic4), value); + QAtomicInt atomic5 = atomic2; + QCOMPARE(int(atomic5), value); +} + +void tst_QAtomicInt::equality_operator_data() +{ + QTest::addColumn("value1"); + QTest::addColumn("value2"); + QTest::addColumn("result"); + + QTest::newRow("success0") << 1 << 1 << 1; + QTest::newRow("success1") << -1 << -1 << 1; + QTest::newRow("failure0") << 0 << 1 << 0; + QTest::newRow("failure1") << 1 << 0 << 0; + QTest::newRow("failure2") << 0 << -1 << 0; + QTest::newRow("failure3") << -1 << 0 << 0; +} + +void tst_QAtomicInt::equality_operator() +{ + QFETCH(int, value1); + QFETCH(int, value2); + QAtomicInt x = value1; + QTEST(x == value2 ? 1 : 0, "result"); +} + +void tst_QAtomicInt::inequality_operator_data() +{ + QTest::addColumn("value1"); + QTest::addColumn("value2"); + QTest::addColumn("result"); + + QTest::newRow("failure0") << 1 << 1 << 0; + QTest::newRow("failure1") << -1 << -1 << 0; + QTest::newRow("success0") << 0 << 1 << 1; + QTest::newRow("success1") << 1 << 0 << 1; + QTest::newRow("success2") << 0 << -1 << 1; + QTest::newRow("success3") << -1 << 0 << 1; +} + +void tst_QAtomicInt::inequality_operator() +{ + QFETCH(int, value1); + QFETCH(int, value2); + QAtomicInt x = value1; + QTEST(x != value2 ? 1 : 0, "result"); +} + +void tst_QAtomicInt::not_operator_data() +{ constructor_data(); } + +void tst_QAtomicInt::not_operator() +{ + QFETCH(int, value); + QAtomicInt atomic = value; + QCOMPARE(!atomic, !value); +} + +void tst_QAtomicInt::cast_operator_data() +{ constructor_data(); } + +void tst_QAtomicInt::cast_operator() +{ + QFETCH(int, value); + QAtomicInt atomic = value; + int copy = atomic; + QCOMPARE(copy, value); +} + +void tst_QAtomicInt::assignment_operator_data() +{ + QTest::addColumn("value"); + QTest::addColumn("newval"); + + QTest::newRow("value0") << 0 << 1; + QTest::newRow("value1") << 1 << 0; + QTest::newRow("value2") << 0 << -1; + QTest::newRow("value3") << -1 << 0; + QTest::newRow("value4") << -1 << 1; + QTest::newRow("value5") << 1 << -1; +} + +void tst_QAtomicInt::assignment_operator() +{ + QFETCH(int, value); + QFETCH(int, newval); + + { + QAtomicInt atomic1 = value; + atomic1 = newval; + QCOMPARE(int(atomic1), newval); + atomic1 = value; + QCOMPARE(int(atomic1), value); + QAtomicInt atomic2 = newval; + atomic1 = atomic2; + QCOMPARE(atomic1, atomic2); + } +} + +void tst_QAtomicInt::isReferenceCountingNative() +{ +#if defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE) + // the runtime test should say the same thing + QVERIFY(QAtomicInt::isReferenceCountingNative()); + +# if (defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE) \ + || defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_INT_REFERENCE_COUNTING_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE) + // could be either, just want to make sure the function is implemented + QVERIFY(QAtomicInt::isReferenceCountingNative() || !QAtomicInt::isReferenceCountingNative()); + +# if (defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_INT_REFERENCE_COUNTING_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE) + // the runtime test should say the same thing + QVERIFY(!QAtomicInt::isReferenceCountingNative()); + +# if (defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE)) +# error "Define only one of Q_ATOMIC_INT_REFERENCE_COUNTING_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#else +# error "Q_ATOMIC_INT_REFERENCE_COUNTING_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined" +#endif +} + +void tst_QAtomicInt::isReferenceCountingWaitFree() +{ +#if defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_WAIT_FREE) + // the runtime test should say the same thing + QVERIFY(QAtomicInt::isReferenceCountingWaitFree()); + + // enforce some invariants + QVERIFY(QAtomicInt::isReferenceCountingNative()); +# if defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE) +# error "Reference counting cannot be wait-free and unsupported at the same time!" +# endif +#else + // the runtime test should say the same thing + QVERIFY(!QAtomicInt::isReferenceCountingWaitFree()); +#endif +} + +void tst_QAtomicInt::ref_data() +{ + QTest::addColumn("value"); + QTest::addColumn("result"); + QTest::addColumn("expected"); + + QTest::newRow("data0") << 0 << 1 << 1; + QTest::newRow("data1") << -1 << 0 << 0; + QTest::newRow("data2") << 1 << 1 << 2; +} + +void tst_QAtomicInt::ref() +{ + QFETCH(int, value); + QAtomicInt x = value; + QTEST(x.ref() ? 1 : 0, "result"); + QTEST(int(x), "expected"); +} + +void tst_QAtomicInt::deref_data() +{ + QTest::addColumn("value"); + QTest::addColumn("result"); + QTest::addColumn("expected"); + + QTest::newRow("data0") << 0 << 1 << -1; + QTest::newRow("data1") << 1 << 0 << 0; + QTest::newRow("data2") << 2 << 1 << 1; +} + +void tst_QAtomicInt::deref() +{ + QFETCH(int, value); + QAtomicInt x = value; + QTEST(x.deref() ? 1 : 0, "result"); + QTEST(int(x), "expected"); +} + +void tst_QAtomicInt::isTestAndSetNative() +{ +#if defined(Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE) + // the runtime test should say the same thing + QVERIFY(QAtomicInt::isTestAndSetNative()); + +# if (defined(Q_ATOMIC_INT_TEST_AND_SET_IS_SOMETIMES_NATIVE) \ + || defined(Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_INT_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_INT_TEST_AND_SET_IS_SOMETIMES_NATIVE) + // could be either, just want to make sure the function is implemented + QVERIFY(QAtomicInt::isTestAndSetNative() || !QAtomicInt::isTestAndSetNative()); + +# if (defined(Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_INT_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE) + // the runtime test should say the same thing + QVERIFY(!QAtomicInt::isTestAndSetNative()); + +# if (defined(Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_INT_TEST_AND_SET_IS_SOMETIMES_NATIVE)) +# error "Define only one of Q_ATOMIC_INT_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#else +# error "Q_ATOMIC_INT_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined" +#endif +} + +void tst_QAtomicInt::isTestAndSetWaitFree() +{ +#if defined(Q_ATOMIC_INT_TEST_AND_SET_IS_WAIT_FREE) + // the runtime test should say the same thing + QVERIFY(QAtomicInt::isTestAndSetWaitFree()); + + // enforce some invariants + QVERIFY(QAtomicInt::isTestAndSetNative()); +# if defined(Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE) +# error "Reference counting cannot be wait-free and unsupported at the same time!" +# endif +#else + // the runtime test should say the same thing + QVERIFY(!QAtomicInt::isTestAndSetWaitFree()); +#endif +} + +void tst_QAtomicInt::testAndSet_data() +{ + QTest::addColumn("value"); + QTest::addColumn("expected"); + QTest::addColumn("newval"); + QTest::addColumn("result"); + + // these should succeed + QTest::newRow("success0") << 0 << 0 << 0 << 1; + QTest::newRow("success1") << 0 << 0 << 1 << 1; + QTest::newRow("success2") << 0 << 0 << -1 << 1; + QTest::newRow("success3") << 1 << 1 << 0 << 1; + QTest::newRow("success4") << 1 << 1 << 1 << 1; + QTest::newRow("success5") << 1 << 1 << -1 << 1; + QTest::newRow("success6") << -1 << -1 << 0 << 1; + QTest::newRow("success7") << -1 << -1 << 1 << 1; + QTest::newRow("success8") << -1 << -1 << -1 << 1; + QTest::newRow("success9") << INT_MIN+1 << INT_MIN+1 << INT_MIN+1 << 1; + QTest::newRow("successA") << INT_MIN+1 << INT_MIN+1 << 1 << 1; + QTest::newRow("successB") << INT_MIN+1 << INT_MIN+1 << -1 << 1; + QTest::newRow("successC") << INT_MAX << INT_MAX << INT_MAX << 1; + QTest::newRow("successD") << INT_MAX << INT_MAX << 1 << 1; + QTest::newRow("successE") << INT_MAX << INT_MAX << -1 << 1; + + // these should fail + QTest::newRow("failure0") << 0 << 1 << ~0 << 0; + QTest::newRow("failure1") << 0 << -1 << ~0 << 0; + QTest::newRow("failure2") << 1 << 0 << ~0 << 0; + QTest::newRow("failure3") << -1 << 0 << ~0 << 0; + QTest::newRow("failure4") << 1 << -1 << ~0 << 0; + QTest::newRow("failure5") << -1 << 1 << ~0 << 0; + QTest::newRow("failure6") << INT_MIN+1 << INT_MAX << ~0 << 0; + QTest::newRow("failure7") << INT_MAX << INT_MIN+1 << ~0 << 0; +} + +void tst_QAtomicInt::testAndSet() +{ + QFETCH(int, value); + QFETCH(int, expected); + QFETCH(int, newval); + + { + QAtomicInt atomic = value; + QTEST(atomic.testAndSetRelaxed(expected, newval) ? 1 : 0, "result"); + } + + { + QAtomicInt atomic = value; + QTEST(atomic.testAndSetAcquire(expected, newval) ? 1 : 0, "result"); + } + + { + QAtomicInt atomic = value; + QTEST(atomic.testAndSetRelease(expected, newval) ? 1 : 0, "result"); + } + + { + QAtomicInt atomic = value; + QTEST(atomic.testAndSetOrdered(expected, newval) ? 1 : 0, "result"); + } +} + +void tst_QAtomicInt::isFetchAndStoreNative() +{ +#if defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE) + // the runtime test should say the same thing + QVERIFY(QAtomicInt::isFetchAndStoreNative()); + +# if (defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_SOMETIMES_NATIVE) \ + || defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_INT_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_SOMETIMES_NATIVE) + // could be either, just want to make sure the function is implemented + QVERIFY(QAtomicInt::isFetchAndStoreNative() || !QAtomicInt::isFetchAndStoreNative()); + +# if (defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_INT_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE) + // the runtime test should say the same thing + QVERIFY(!QAtomicInt::isFetchAndStoreNative()); + +# if (defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_SOMETIMES_NATIVE)) +# error "Define only one of Q_ATOMIC_INT_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#else +# error "Q_ATOMIC_INT_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined" +#endif +} + +void tst_QAtomicInt::isFetchAndStoreWaitFree() +{ +#if defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_WAIT_FREE) + // the runtime test should say the same thing + QVERIFY(QAtomicInt::isFetchAndStoreWaitFree()); + + // enforce some invariants + QVERIFY(QAtomicInt::isFetchAndStoreNative()); +# if defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE) +# error "Reference counting cannot be wait-free and unsupported at the same time!" +# endif +#else + // the runtime test should say the same thing + QVERIFY(!QAtomicInt::isFetchAndStoreWaitFree()); +#endif +} + +void tst_QAtomicInt::fetchAndStore_data() +{ + QTest::addColumn("value"); + QTest::addColumn("newval"); + + QTest::newRow("data0") << 0 << 1; + QTest::newRow("data1") << 1 << 2; + QTest::newRow("data2") << 3 << 8; +} + +void tst_QAtomicInt::fetchAndStore() +{ + QFETCH(int, value); + QFETCH(int, newval); + + { + QAtomicInt atomic = value; + QCOMPARE(atomic.fetchAndStoreRelaxed(newval), value); + QCOMPARE(int(atomic), newval); + } + + { + QAtomicInt atomic = value; + QCOMPARE(atomic.fetchAndStoreAcquire(newval), value); + QCOMPARE(int(atomic), newval); + } + + { + QAtomicInt atomic = value; + QCOMPARE(atomic.fetchAndStoreRelease(newval), value); + QCOMPARE(int(atomic), newval); + } + + { + QAtomicInt atomic = value; + QCOMPARE(atomic.fetchAndStoreOrdered(newval), value); + QCOMPARE(int(atomic), newval); + } +} + +void tst_QAtomicInt::isFetchAndAddNative() +{ +#if defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE) + // the runtime test should say the same thing + QVERIFY(QAtomicInt::isFetchAndAddNative()); + +# if (defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_SOMETIMES_NATIVE) \ + || defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_INT_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_SOMETIMES_NATIVE) + // could be either, just want to make sure the function is implemented + QVERIFY(QAtomicInt::isFetchAndAddNative() || !QAtomicInt::isFetchAndAddNative()); + +# if (defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_INT_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE) + // the runtime test should say the same thing + QVERIFY(!QAtomicInt::isFetchAndAddNative()); + +# if (defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_SOMETIMES_NATIVE)) +# error "Define only one of Q_ATOMIC_INT_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#else +# error "Q_ATOMIC_INT_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined" +#endif +} + +void tst_QAtomicInt::isFetchAndAddWaitFree() +{ +#if defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_WAIT_FREE) + // the runtime test should say the same thing + QVERIFY(QAtomicInt::isFetchAndAddWaitFree()); + + // enforce some invariants + QVERIFY(QAtomicInt::isFetchAndAddNative()); +# if defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE) +# error "Reference counting cannot be wait-free and unsupported at the same time!" +# endif +#else + // the runtime test should say the same thing + QVERIFY(!QAtomicInt::isFetchAndAddWaitFree()); +#endif +} + +void tst_QAtomicInt::fetchAndAdd_data() +{ + QTest::addColumn("value1"); + QTest::addColumn("value2"); + + QTest::newRow("0+1") << 0 << 1; + QTest::newRow("1+0") << 1 << 0; + QTest::newRow("1+2") << 1 << 2; + QTest::newRow("2+1") << 2 << 1; + QTest::newRow("10+21") << 10 << 21; + QTest::newRow("31+40") << 31 << 40; + QTest::newRow("51+62") << 51 << 62; + QTest::newRow("72+81") << 72 << 81; + QTest::newRow("810+721") << 810 << 721; + QTest::newRow("631+540") << 631 << 540; + QTest::newRow("451+362") << 451 << 362; + QTest::newRow("272+181") << 272 << 181; + QTest::newRow("1810+8721") << 1810 << 8721; + QTest::newRow("3631+6540") << 3631 << 6540; + QTest::newRow("5451+4362") << 5451 << 4362; + QTest::newRow("7272+2181") << 7272 << 2181; + + QTest::newRow("0+-1") << 0 << -1; + QTest::newRow("1+0") << 1 << 0; + QTest::newRow("1+-2") << 1 << -2; + QTest::newRow("2+-1") << 2 << -1; + QTest::newRow("10+-21") << 10 << -21; + QTest::newRow("31+-40") << 31 << -40; + QTest::newRow("51+-62") << 51 << -62; + QTest::newRow("72+-81") << 72 << -81; + QTest::newRow("810+-721") << 810 << -721; + QTest::newRow("631+-540") << 631 << -540; + QTest::newRow("451+-362") << 451 << -362; + QTest::newRow("272+-181") << 272 << -181; + QTest::newRow("1810+-8721") << 1810 << -8721; + QTest::newRow("3631+-6540") << 3631 << -6540; + QTest::newRow("5451+-4362") << 5451 << -4362; + QTest::newRow("7272+-2181") << 7272 << -2181; + + QTest::newRow("0+1") << 0 << 1; + QTest::newRow("-1+0") << -1 << 0; + QTest::newRow("-1+2") << -1 << 2; + QTest::newRow("-2+1") << -2 << 1; + QTest::newRow("-10+21") << -10 << 21; + QTest::newRow("-31+40") << -31 << 40; + QTest::newRow("-51+62") << -51 << 62; + QTest::newRow("-72+81") << -72 << 81; + QTest::newRow("-810+721") << -810 << 721; + QTest::newRow("-631+540") << -631 << 540; + QTest::newRow("-451+362") << -451 << 362; + QTest::newRow("-272+181") << -272 << 181; + QTest::newRow("-1810+8721") << -1810 << 8721; + QTest::newRow("-3631+6540") << -3631 << 6540; + QTest::newRow("-5451+4362") << -5451 << 4362; + QTest::newRow("-7272+2181") << -7272 << 2181; +} + +void tst_QAtomicInt::fetchAndAdd() +{ + QFETCH(int, value1); + QFETCH(int, value2); + int result; + + { + QAtomicInt atomic = value1; + result = atomic.fetchAndAddRelaxed(value2); + QCOMPARE(result, value1); + QCOMPARE(int(atomic), value1 + value2); + } + + { + QAtomicInt atomic = value1; + result = atomic.fetchAndAddAcquire(value2); + QCOMPARE(result, value1); + QCOMPARE(int(atomic), value1 + value2); + } + + { + QAtomicInt atomic = value1; + result = atomic.fetchAndAddRelease(value2); + QCOMPARE(result, value1); + QCOMPARE(int(atomic), value1 + value2); + } + + { + QAtomicInt atomic = value1; + result = atomic.fetchAndAddOrdered(value2); + QCOMPARE(result, value1); + QCOMPARE(int(atomic), value1 + value2); + } +} + +void tst_QAtomicInt::testAndSet_loop() +{ + QTime stopWatch; + stopWatch.start(); + + int iterations = 10000000; + + QAtomicInt val=0; + for (int i = 0; i < iterations; ++i) { + QVERIFY(val.testAndSetRelaxed(val, val+1)); + if ((i % 1000) == 999) { + if (stopWatch.elapsed() > 60 * 1000) { + // This test shouldn't run for more than two minutes. + qDebug("Interrupted test after %d iterations (%.2f iterations/sec)", + i, (i * 1000.0) / double(stopWatch.elapsed())); + break; + } + } + } +} + +void tst_QAtomicInt::fetchAndAdd_loop() +{ + int iterations = 10000000; +#if defined (Q_OS_HPUX) + iterations = 1000000; +#endif + + QAtomicInt val=0; + for (int i = 0; i < iterations; ++i) { + const int prev = val.fetchAndAddRelaxed(1); + QCOMPARE(prev, int(val) -1); + } +} + +class FetchAndAddThread : public QThread +{ +public: + void run() + { + + for (int i = 0; i < iterations; ++i) + val->fetchAndAddAcquire(1); + + for (int i = 0; i < iterations; ++i) + val->fetchAndAddAcquire(-1); + + } +QAtomicInt *val; +int iterations; +}; + + +void tst_QAtomicInt::fetchAndAdd_threadedLoop() +{ + QAtomicInt val; + FetchAndAddThread t1; + t1.val = &val; + t1.iterations = 1000000; + + FetchAndAddThread t2; + t2.val = &val; + t2.iterations = 2000000; + + t1.start(); + t2.start(); + t1.wait(); + t2.wait(); + + QCOMPARE(int(val), 0); +} + +QTEST_MAIN(tst_QAtomicInt) +#include "tst_qatomicint.moc" diff --git a/tests/auto/corelib/thread/qatomicpointer/.gitignore b/tests/auto/corelib/thread/qatomicpointer/.gitignore new file mode 100644 index 0000000000..2843c40749 --- /dev/null +++ b/tests/auto/corelib/thread/qatomicpointer/.gitignore @@ -0,0 +1 @@ +tst_qatomicpointer diff --git a/tests/auto/corelib/thread/qatomicpointer/qatomicpointer.pro b/tests/auto/corelib/thread/qatomicpointer/qatomicpointer.pro new file mode 100644 index 0000000000..89ff137d39 --- /dev/null +++ b/tests/auto/corelib/thread/qatomicpointer/qatomicpointer.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +SOURCES += tst_qatomicpointer.cpp +QT = core +CONFIG += parallel_test diff --git a/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp new file mode 100644 index 0000000000..42e744aaa6 --- /dev/null +++ b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp @@ -0,0 +1,674 @@ +/**************************************************************************** +** +** 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 + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QAtomicPointer : public QObject +{ + Q_OBJECT + +public: + tst_QAtomicPointer(); + ~tst_QAtomicPointer(); + +private slots: + void warningFree(); + + void constructor(); + void copy_constructor(); + void equality_operator(); + void inequality_operator(); + void assignment_operator(); + void star_operator(); + void dereference_operator(); + + void isTestAndSetNative(); + void isTestAndSetWaitFree(); + void testAndSet(); + + void isFetchAndStoreNative(); + void isFetchAndStoreWaitFree(); + void fetchAndStore(); + + void isFetchAndAddNative(); + void isFetchAndAddWaitFree(); + void fetchAndAdd_data(); + void fetchAndAdd(); + +private: + static void warningFreeHelper(); +}; + +tst_QAtomicPointer::tst_QAtomicPointer() +{ } + +tst_QAtomicPointer::~tst_QAtomicPointer() +{ } + +struct WFHC +{ + void bar() {} +}; + +void tst_QAtomicPointer::warningFreeHelper() +{ + qFatal("This code is bogus, and shouldn't be run. We're looking for compiler warnings only."); + + QBasicAtomicPointer p = Q_BASIC_ATOMIC_INITIALIZER(0); + + p->bar(); + + WFHC *expectedValue = 0; + WFHC *newValue = 0; + qptrdiff valueToAdd = 0; + + p.testAndSetRelaxed(expectedValue, newValue); + p.testAndSetAcquire(expectedValue, newValue); + p.testAndSetRelease(expectedValue, newValue); + p.testAndSetOrdered(expectedValue, newValue); + + p.fetchAndStoreRelaxed(newValue); + p.fetchAndStoreAcquire(newValue); + p.fetchAndStoreRelease(newValue); + p.fetchAndStoreOrdered(newValue); + + p.fetchAndAddRelaxed(valueToAdd); + p.fetchAndAddAcquire(valueToAdd); + p.fetchAndAddRelease(valueToAdd); + p.fetchAndAddOrdered(valueToAdd); +} + +void tst_QAtomicPointer::warningFree() +{ + // This is a compile time check for warnings. + // No need for actual work here. + + void (*foo)() = &warningFreeHelper; + (void)foo; +} + +void tst_QAtomicPointer::constructor() +{ + void *one = this; + QAtomicPointer atomic1 = one; + QVERIFY(atomic1 == one); + + void *two = &one; + QAtomicPointer atomic2 = two; + QVERIFY(atomic2 == two); + + void *three = &two; + QAtomicPointer atomic3 = three; + QVERIFY(atomic3 == three); +} + +void tst_QAtomicPointer::copy_constructor() +{ + void *one = this; + QAtomicPointer atomic1 = one; + QAtomicPointer atomic1_copy = atomic1; + QVERIFY(atomic1_copy == one); + QCOMPARE(atomic1_copy, atomic1); + + void *two = &one; + QAtomicPointer atomic2 = two; + QAtomicPointer atomic2_copy = atomic2; + QVERIFY(atomic2_copy == two); + QCOMPARE(atomic2_copy, atomic2); + + void *three = &two; + QAtomicPointer atomic3 = three; + QAtomicPointer atomic3_copy = atomic3; + QVERIFY(atomic3_copy == three); + QCOMPARE(atomic3_copy, atomic3); +} + +void tst_QAtomicPointer::equality_operator() +{ + void *one = this; + void *two = &one; + void *three = &two; + + QAtomicPointer atomic1 = one; + QAtomicPointer atomic2 = two; + QAtomicPointer atomic3 = three; + + QVERIFY(atomic1 == one); + QVERIFY(!(atomic1 == two)); + QVERIFY(!(atomic1 == three)); + + QVERIFY(!(atomic2 == one)); + QVERIFY(atomic2 == two); + QVERIFY(!(atomic2 == three)); + + QVERIFY(!(atomic3 == one)); + QVERIFY(!(atomic3 == two)); + QVERIFY(atomic3 == three); +} + +void tst_QAtomicPointer::inequality_operator() +{ + void *one = this; + void *two = &one; + void *three = &two; + + QAtomicPointer atomic1 = one; + QAtomicPointer atomic2 = two; + QAtomicPointer atomic3 = three; + + QVERIFY(!(atomic1 != one)); + QVERIFY(atomic1 != two); + QVERIFY(atomic1 != three); + + QVERIFY(atomic2 != one); + QVERIFY(!(atomic2 != two)); + QVERIFY(atomic2 != three); + + QVERIFY(atomic3 != one); + QVERIFY(atomic3 != two); + QVERIFY(!(atomic3 != three)); +} + +void tst_QAtomicPointer::assignment_operator() +{ + void *one = this; + void *two = &one; + void *three = &two; + + QAtomicPointer atomic1 = one; + QAtomicPointer atomic2 = two; + QAtomicPointer atomic3 = three; + + QVERIFY(atomic1 == one); + QVERIFY(atomic2 == two); + QVERIFY(atomic3 == three); + + atomic1 = two; + atomic2 = three; + atomic3 = one; + + QVERIFY(atomic1 == two); + QVERIFY(atomic2 == three); + QVERIFY(atomic3 == one); +} + +struct Type +{ + inline const Type *self() const + { return this; } +}; + +void tst_QAtomicPointer::star_operator() +{ + Type t; + QAtomicPointer p = &t; + QCOMPARE((*p).self(), t.self()); +} + +void tst_QAtomicPointer::dereference_operator() +{ + Type t; + QAtomicPointer p = &t; + QCOMPARE(p->self(), t.self()); +} + +void tst_QAtomicPointer::isTestAndSetNative() +{ +#if defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE) + // the runtime test should say the same thing + QVERIFY(QAtomicPointer::isTestAndSetNative()); + +# if (defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_SOMETIMES_NATIVE) \ + || defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_POINTER_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_SOMETIMES_NATIVE) + // could be either, just want to make sure the function is implemented + QVERIFY(QAtomicPointer::isTestAndSetNative() + || !QAtomicPointer::isTestAndSetNative()); + +# if (defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_POINTER_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE) + // the runtime test should say the same thing + QVERIFY(!QAtomicPointer::isTestAndSetNative()); + +# if (defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_SOMETIMES_NATIVE)) +# error "Define only one of Q_ATOMIC_POINTER_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#else +# error "Q_ATOMIC_POINTER_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined" +#endif +} + +void tst_QAtomicPointer::isTestAndSetWaitFree() +{ +#if defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_WAIT_FREE) + // the runtime test should say the same thing + QVERIFY(QAtomicPointer::isTestAndSetWaitFree()); + + // enforce some invariants + QVERIFY(QAtomicPointer::isTestAndSetNative()); +# if defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE) +# error "Reference counting cannot be wait-free and unsupported at the same time!" +# endif +#else + // the runtime test should say the same thing + QVERIFY(!QAtomicPointer::isTestAndSetWaitFree()); +#endif +} + +void tst_QAtomicPointer::testAndSet() +{ + void *one = this; + void *two = &one; + void *three = &two; + + { + QAtomicPointer atomic1 = one; + QAtomicPointer atomic2 = two; + QAtomicPointer atomic3 = three; + + QVERIFY(atomic1 == one); + QVERIFY(atomic2 == two); + QVERIFY(atomic3 == three); + + QVERIFY(atomic1.testAndSetRelaxed(one, two)); + QVERIFY(atomic2.testAndSetRelaxed(two, three)); + QVERIFY(atomic3.testAndSetRelaxed(three, one)); + + QVERIFY(atomic1 == two); + QVERIFY(atomic2 == three); + QVERIFY(atomic3 == one); + } + + { + QAtomicPointer atomic1 = one; + QAtomicPointer atomic2 = two; + QAtomicPointer atomic3 = three; + + QVERIFY(atomic1 == one); + QVERIFY(atomic2 == two); + QVERIFY(atomic3 == three); + + QVERIFY(atomic1.testAndSetAcquire(one, two)); + QVERIFY(atomic2.testAndSetAcquire(two, three)); + QVERIFY(atomic3.testAndSetAcquire(three, one)); + + QVERIFY(atomic1 == two); + QVERIFY(atomic2 == three); + QVERIFY(atomic3 == one); + } + + { + QAtomicPointer atomic1 = one; + QAtomicPointer atomic2 = two; + QAtomicPointer atomic3 = three; + + QVERIFY(atomic1 == one); + QVERIFY(atomic2 == two); + QVERIFY(atomic3 == three); + + QVERIFY(atomic1.testAndSetRelease(one, two)); + QVERIFY(atomic2.testAndSetRelease(two, three)); + QVERIFY(atomic3.testAndSetRelease(three, one)); + + QVERIFY(atomic1 == two); + QVERIFY(atomic2 == three); + QVERIFY(atomic3 == one); + } + + { + QAtomicPointer atomic1 = one; + QAtomicPointer atomic2 = two; + QAtomicPointer atomic3 = three; + + QVERIFY(atomic1 == one); + QVERIFY(atomic2 == two); + QVERIFY(atomic3 == three); + + QVERIFY(atomic1.testAndSetOrdered(one, two)); + QVERIFY(atomic2.testAndSetOrdered(two, three)); + QVERIFY(atomic3.testAndSetOrdered(three, one)); + + QVERIFY(atomic1 == two); + QVERIFY(atomic2 == three); + QVERIFY(atomic3 == one); + } +} + +void tst_QAtomicPointer::isFetchAndStoreNative() +{ +#if defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE) + // the runtime test should say the same thing + QVERIFY(QAtomicPointer::isFetchAndStoreNative()); + +# if (defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_SOMETIMES_NATIVE) \ + || defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_SOMETIMES_NATIVE) + // could be either, just want to make sure the function is implemented + QVERIFY(QAtomicPointer::isFetchAndStoreNative() + || !QAtomicPointer::isFetchAndStoreNative()); + +# if (defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_NOT_NATIVE) + // the runtime test should say the same thing + QVERIFY(!QAtomicPointer::isFetchAndStoreNative()); + +# if (defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_SOMETIMES_NATIVE)) +# error "Define only one of Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#else +# error "Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined" +#endif +} + +void tst_QAtomicPointer::isFetchAndStoreWaitFree() +{ +#if defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_WAIT_FREE) + // the runtime test should say the same thing + QVERIFY(QAtomicPointer::isFetchAndStoreWaitFree()); + + // enforce some invariants + QVERIFY(QAtomicPointer::isFetchAndStoreNative()); +# if defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_NOT_NATIVE) +# error "Reference counting cannot be wait-free and unsupported at the same time!" +# endif +#else + // the runtime test should say the same thing + QVERIFY(!QAtomicPointer::isFetchAndStoreWaitFree()); +#endif +} + +void tst_QAtomicPointer::fetchAndStore() +{ + void *one = this; + void *two = &one; + void *three = &two; + + { + QAtomicPointer atomic1 = one; + QAtomicPointer atomic2 = two; + QAtomicPointer atomic3 = three; + + QVERIFY(atomic1 == one); + QVERIFY(atomic2 == two); + QVERIFY(atomic3 == three); + + QCOMPARE(atomic1.fetchAndStoreRelaxed(two), one); + QCOMPARE(atomic2.fetchAndStoreRelaxed(three), two); + QCOMPARE(atomic3.fetchAndStoreRelaxed(one), three); + + QVERIFY(atomic1 == two); + QVERIFY(atomic2 == three); + QVERIFY(atomic3 == one); + } + + { + QAtomicPointer atomic1 = one; + QAtomicPointer atomic2 = two; + QAtomicPointer atomic3 = three; + + QVERIFY(atomic1 == one); + QVERIFY(atomic2 == two); + QVERIFY(atomic3 == three); + + QCOMPARE(atomic1.fetchAndStoreAcquire(two), one); + QCOMPARE(atomic2.fetchAndStoreAcquire(three), two); + QCOMPARE(atomic3.fetchAndStoreAcquire(one), three); + + QVERIFY(atomic1 == two); + QVERIFY(atomic2 == three); + QVERIFY(atomic3 == one); + } + + { + QAtomicPointer atomic1 = one; + QAtomicPointer atomic2 = two; + QAtomicPointer atomic3 = three; + + QVERIFY(atomic1 == one); + QVERIFY(atomic2 == two); + QVERIFY(atomic3 == three); + + QCOMPARE(atomic1.fetchAndStoreRelease(two), one); + QCOMPARE(atomic2.fetchAndStoreRelease(three), two); + QCOMPARE(atomic3.fetchAndStoreRelease(one), three); + + QVERIFY(atomic1 == two); + QVERIFY(atomic2 == three); + QVERIFY(atomic3 == one); + } + + { + QAtomicPointer atomic1 = one; + QAtomicPointer atomic2 = two; + QAtomicPointer atomic3 = three; + + QVERIFY(atomic1 == one); + QVERIFY(atomic2 == two); + QVERIFY(atomic3 == three); + + QCOMPARE(atomic1.fetchAndStoreOrdered(two), one); + QCOMPARE(atomic2.fetchAndStoreOrdered(three), two); + QCOMPARE(atomic3.fetchAndStoreOrdered(one), three); + + QVERIFY(atomic1 == two); + QVERIFY(atomic2 == three); + QVERIFY(atomic3 == one); + } +} + +void tst_QAtomicPointer::isFetchAndAddNative() +{ +#if defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_ALWAYS_NATIVE) + // the runtime test should say the same thing + QVERIFY(QAtomicPointer::isFetchAndAddNative()); + +# if (defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_SOMETIMES_NATIVE) \ + || defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_SOMETIMES_NATIVE) + // could be either, just want to make sure the function is implemented + QVERIFY(QAtomicPointer::isFetchAndAddNative() + || !QAtomicPointer::isFetchAndAddNative()); + +# if (defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE)) +# error "Define only one of Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#elif defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE) + // the runtime test should say the same thing + QVERIFY(!QAtomicPointer::isFetchAndAddNative()); + +# if (defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_ALWAYS_NATIVE) \ + || defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_SOMETIMES_NATIVE)) +# error "Define only one of Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE" +# endif +#else +# error "Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined" +#endif +} + +void tst_QAtomicPointer::isFetchAndAddWaitFree() +{ +#if defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_WAIT_FREE) + // the runtime test should say the same thing + QVERIFY(QAtomicPointer::isFetchAndAddWaitFree()); + + // enforce some invariants + QVERIFY(QAtomicPointer::isFetchAndAddNative()); +# if defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE) +# error "Reference counting cannot be wait-free and unsupported at the same time!" +# endif +#else + // the runtime test should say the same thing + QVERIFY(!QAtomicPointer::isFetchAndAddWaitFree()); +#endif +} + +void tst_QAtomicPointer::fetchAndAdd_data() +{ + QTest::addColumn("valueToAdd"); + + QTest::newRow("0") << 0; + QTest::newRow("1") << 1; + QTest::newRow("2") << 2; + QTest::newRow("10") << 10; + QTest::newRow("31") << 31; + QTest::newRow("51") << 51; + QTest::newRow("72") << 72; + QTest::newRow("810") << 810; + QTest::newRow("631") << 631; + QTest::newRow("451") << 451; + QTest::newRow("272") << 272; + QTest::newRow("1810") << 1810; + QTest::newRow("3631") << 3631; + QTest::newRow("5451") << 5451; + QTest::newRow("7272") << 7272; + QTest::newRow("-1") << -1; + QTest::newRow("-2") << -2; + QTest::newRow("-10") << -10; + QTest::newRow("-31") << -31; + QTest::newRow("-51") << -51; + QTest::newRow("-72") << -72; + QTest::newRow("-810") << -810; + QTest::newRow("-631") << -631; + QTest::newRow("-451") << -451; + QTest::newRow("-272") << -272; + QTest::newRow("-1810") << -1810; + QTest::newRow("-3631") << -3631; + QTest::newRow("-5451") << -5451; + QTest::newRow("-7272") << -7272; +} + +void tst_QAtomicPointer::fetchAndAdd() +{ + QFETCH(int, valueToAdd); + + char c; + char *pc = &c; + short s; + short *ps = &s; + int i; + int *pi = &i; + + { + QAtomicPointer pointer1 = pc; + QCOMPARE(quintptr(pointer1.fetchAndAddRelaxed(valueToAdd)), quintptr(pc)); + QCOMPARE(quintptr(pointer1.fetchAndAddRelaxed(-valueToAdd)), quintptr(pc + valueToAdd)); + QCOMPARE(quintptr(static_cast(pointer1)), quintptr(pc)); + QAtomicPointer pointer2 = ps; + QCOMPARE(quintptr(pointer2.fetchAndAddRelaxed(valueToAdd)), quintptr(ps)); + QCOMPARE(quintptr(pointer2.fetchAndAddRelaxed(-valueToAdd)), quintptr(ps + valueToAdd)); + QCOMPARE(quintptr(static_cast(pointer2)), quintptr(ps)); + QAtomicPointer pointer3 = pi; + QCOMPARE(quintptr(pointer3.fetchAndAddRelaxed(valueToAdd)), quintptr(pi)); + QCOMPARE(quintptr(pointer3.fetchAndAddRelaxed(-valueToAdd)), quintptr(pi + valueToAdd)); + QCOMPARE(quintptr(static_cast(pointer3)), quintptr(pi)); + } + + { + QAtomicPointer pointer1 = pc; + QCOMPARE(quintptr(pointer1.fetchAndAddAcquire(valueToAdd)), quintptr(pc)); + QCOMPARE(quintptr(pointer1.fetchAndAddAcquire(-valueToAdd)), quintptr(pc + valueToAdd)); + QCOMPARE(quintptr(static_cast(pointer1)), quintptr(pc)); + QAtomicPointer pointer2 = ps; + QCOMPARE(quintptr(pointer2.fetchAndAddAcquire(valueToAdd)), quintptr(ps)); + QCOMPARE(quintptr(pointer2.fetchAndAddAcquire(-valueToAdd)), quintptr(ps + valueToAdd)); + QCOMPARE(quintptr(static_cast(pointer2)), quintptr(ps)); + QAtomicPointer pointer3 = pi; + QCOMPARE(quintptr(pointer3.fetchAndAddAcquire(valueToAdd)), quintptr(pi)); + QCOMPARE(quintptr(pointer3.fetchAndAddAcquire(-valueToAdd)), quintptr(pi + valueToAdd)); + QCOMPARE(quintptr(static_cast(pointer3)), quintptr(pi)); + } + + { + QAtomicPointer pointer1 = pc; + QCOMPARE(quintptr(pointer1.fetchAndAddRelease(valueToAdd)), quintptr(pc)); + QCOMPARE(quintptr(pointer1.fetchAndAddRelease(-valueToAdd)), quintptr(pc + valueToAdd)); + QCOMPARE(quintptr(static_cast(pointer1)), quintptr(pc)); + QAtomicPointer pointer2 = ps; + QCOMPARE(quintptr(pointer2.fetchAndAddRelease(valueToAdd)), quintptr(ps)); + QCOMPARE(quintptr(pointer2.fetchAndAddRelease(-valueToAdd)), quintptr(ps + valueToAdd)); + QCOMPARE(quintptr(static_cast(pointer2)), quintptr(ps)); + QAtomicPointer pointer3 = pi; + QCOMPARE(quintptr(pointer3.fetchAndAddRelease(valueToAdd)), quintptr(pi)); + QCOMPARE(quintptr(pointer3.fetchAndAddRelease(-valueToAdd)), quintptr(pi + valueToAdd)); + QCOMPARE(quintptr(static_cast(pointer3)), quintptr(pi)); + } + + { + QAtomicPointer pointer1 = pc; + QCOMPARE(quintptr(pointer1.fetchAndAddOrdered(valueToAdd)), quintptr(pc)); + QCOMPARE(quintptr(pointer1.fetchAndAddOrdered(-valueToAdd)), quintptr(pc + valueToAdd)); + QCOMPARE(quintptr(static_cast(pointer1)), quintptr(pc)); + QAtomicPointer pointer2 = ps; + QCOMPARE(quintptr(pointer2.fetchAndAddOrdered(valueToAdd)), quintptr(ps)); + QCOMPARE(quintptr(pointer2.fetchAndAddOrdered(-valueToAdd)), quintptr(ps + valueToAdd)); + QCOMPARE(quintptr(static_cast(pointer2)), quintptr(ps)); + QAtomicPointer pointer3 = pi; + QCOMPARE(quintptr(pointer3.fetchAndAddOrdered(valueToAdd)), quintptr(pi)); + QCOMPARE(quintptr(pointer3.fetchAndAddOrdered(-valueToAdd)), quintptr(pi + valueToAdd)); + QCOMPARE(quintptr(static_cast(pointer3)), quintptr(pi)); + } +} + +QTEST_APPLESS_MAIN(tst_QAtomicPointer) +#include "tst_qatomicpointer.moc" diff --git a/tests/auto/corelib/thread/qmutex/.gitignore b/tests/auto/corelib/thread/qmutex/.gitignore new file mode 100644 index 0000000000..2f6b74166f --- /dev/null +++ b/tests/auto/corelib/thread/qmutex/.gitignore @@ -0,0 +1 @@ +tst_qmutex diff --git a/tests/auto/corelib/thread/qmutex/qmutex.pro b/tests/auto/corelib/thread/qmutex/qmutex.pro new file mode 100644 index 0000000000..608b08e16e --- /dev/null +++ b/tests/auto/corelib/thread/qmutex/qmutex.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +SOURCES += tst_qmutex.cpp +QT = core +CONFIG += parallel_test +CONFIG += insignificant_test diff --git a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp new file mode 100644 index 0000000000..7ad6a98a4d --- /dev/null +++ b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp @@ -0,0 +1,640 @@ +/**************************************************************************** +** +** 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 +#include + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QMutex : public QObject +{ + Q_OBJECT + +public: + tst_QMutex(); + virtual ~tst_QMutex(); + +private slots: + void tryLock(); + void lock_unlock_locked_tryLock(); + void stressTest(); + void tryLockRace(); + void qtbug16115_trylock(); + void moreStress(); +}; + +static const int iterations = 100; + +tst_QMutex::tst_QMutex() + +{ +} + +tst_QMutex::~tst_QMutex() +{ +} + +QAtomicInt lockCount(0); +QMutex normalMutex, recursiveMutex(QMutex::Recursive); +QSemaphore testsTurn; +QSemaphore threadsTurn; + +enum { waitTime = 100 }; + +void tst_QMutex::tryLock() +{ + // test non-recursive mutex + { + class Thread : public QThread + { + public: + void run() + { + testsTurn.release(); + + threadsTurn.acquire(); + QVERIFY(!normalMutex.tryLock()); + testsTurn.release(); + + threadsTurn.acquire(); + QVERIFY(normalMutex.tryLock()); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + QVERIFY(!normalMutex.tryLock()); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + normalMutex.unlock(); + testsTurn.release(); + + threadsTurn.acquire(); + QTime timer; + timer.start(); + QVERIFY(!normalMutex.tryLock(waitTime)); + QVERIFY(timer.elapsed() >= waitTime); + testsTurn.release(); + + threadsTurn.acquire(); + timer.start(); + QVERIFY(normalMutex.tryLock(waitTime)); + QVERIFY(timer.elapsed() <= waitTime); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + timer.start(); + QVERIFY(!normalMutex.tryLock(waitTime)); + QVERIFY(timer.elapsed() >= waitTime); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + normalMutex.unlock(); + testsTurn.release(); + + threadsTurn.acquire(); + QVERIFY(!normalMutex.tryLock(0)); + testsTurn.release(); + + threadsTurn.acquire(); + timer.start(); + QVERIFY(normalMutex.tryLock(0)); + QVERIFY(timer.elapsed() < waitTime); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + QVERIFY(!normalMutex.tryLock(0)); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + normalMutex.unlock(); + testsTurn.release(); + + threadsTurn.acquire(); + } + }; + + Thread thread; + thread.start(); + + // thread can't acquire lock + testsTurn.acquire(); + normalMutex.lock(); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + threadsTurn.release(); + + // thread can acquire lock + testsTurn.acquire(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + normalMutex.unlock(); + threadsTurn.release(); + + // thread can't acquire lock, timeout = waitTime + testsTurn.acquire(); + normalMutex.lock(); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + threadsTurn.release(); + + // thread can acquire lock, timeout = waitTime + testsTurn.acquire(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + normalMutex.unlock(); + threadsTurn.release(); + + // thread can't acquire lock, timeout = 0 + testsTurn.acquire(); + normalMutex.lock(); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + threadsTurn.release(); + + // thread can acquire lock, timeout = 0 + testsTurn.acquire(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + normalMutex.unlock(); + threadsTurn.release(); + + // wait for thread to finish + testsTurn.acquire(); + threadsTurn.release(); + thread.wait(); + } + + // test recursive mutex + { + class Thread : public QThread + { + public: + void run() + { + testsTurn.release(); + + threadsTurn.acquire(); + QVERIFY(!recursiveMutex.tryLock()); + testsTurn.release(); + + threadsTurn.acquire(); + QVERIFY(recursiveMutex.tryLock()); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + QVERIFY(recursiveMutex.tryLock()); + QVERIFY(lockCount.testAndSetRelaxed(1, 2)); + QVERIFY(lockCount.testAndSetRelaxed(2, 1)); + recursiveMutex.unlock(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + recursiveMutex.unlock(); + testsTurn.release(); + + threadsTurn.acquire(); + QTime timer; + timer.start(); + QVERIFY(!recursiveMutex.tryLock(waitTime)); + QVERIFY(timer.elapsed() >= waitTime); + QVERIFY(!recursiveMutex.tryLock(0)); + testsTurn.release(); + + threadsTurn.acquire(); + timer.start(); + QVERIFY(recursiveMutex.tryLock(waitTime)); + QVERIFY(timer.elapsed() <= waitTime); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + QVERIFY(recursiveMutex.tryLock(waitTime)); + QVERIFY(lockCount.testAndSetRelaxed(1, 2)); + QVERIFY(lockCount.testAndSetRelaxed(2, 1)); + recursiveMutex.unlock(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + recursiveMutex.unlock(); + testsTurn.release(); + + threadsTurn.acquire(); + QVERIFY(!recursiveMutex.tryLock(0)); + QVERIFY(!recursiveMutex.tryLock(0)); + testsTurn.release(); + + threadsTurn.acquire(); + timer.start(); + QVERIFY(recursiveMutex.tryLock(0)); + QVERIFY(timer.elapsed() < waitTime); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + QVERIFY(recursiveMutex.tryLock(0)); + QVERIFY(lockCount.testAndSetRelaxed(1, 2)); + QVERIFY(lockCount.testAndSetRelaxed(2, 1)); + recursiveMutex.unlock(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + recursiveMutex.unlock(); + testsTurn.release(); + + threadsTurn.acquire(); + } + }; + + Thread thread; + thread.start(); + + // thread can't acquire lock + testsTurn.acquire(); + recursiveMutex.lock(); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + recursiveMutex.lock(); + QVERIFY(lockCount.testAndSetRelaxed(1, 2)); + threadsTurn.release(); + + // thread can acquire lock + testsTurn.acquire(); + QVERIFY(lockCount.testAndSetRelaxed(2, 1)); + recursiveMutex.unlock(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + recursiveMutex.unlock(); + threadsTurn.release(); + + // thread can't acquire lock, timeout = waitTime + testsTurn.acquire(); + recursiveMutex.lock(); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + recursiveMutex.lock(); + QVERIFY(lockCount.testAndSetRelaxed(1, 2)); + threadsTurn.release(); + + // thread can acquire lock, timeout = waitTime + testsTurn.acquire(); + QVERIFY(lockCount.testAndSetRelaxed(2, 1)); + recursiveMutex.unlock(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + recursiveMutex.unlock(); + threadsTurn.release(); + + // thread can't acquire lock, timeout = 0 + testsTurn.acquire(); + recursiveMutex.lock(); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + recursiveMutex.lock(); + QVERIFY(lockCount.testAndSetRelaxed(1, 2)); + threadsTurn.release(); + + // thread can acquire lock, timeout = 0 + testsTurn.acquire(); + QVERIFY(lockCount.testAndSetRelaxed(2, 1)); + recursiveMutex.unlock(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + recursiveMutex.unlock(); + threadsTurn.release(); + + // stop thread + testsTurn.acquire(); + threadsTurn.release(); + thread.wait(); + } +} + +class mutex_Thread : public QThread +{ +public: + QMutex mutex; + QWaitCondition cond; + + QMutex &test_mutex; + + inline mutex_Thread(QMutex &m) : test_mutex(m) { } + + void run() + { + test_mutex.lock(); + + mutex.lock(); + for (int i = 0; i < iterations; ++i) { + cond.wakeOne(); + cond.wait(&mutex); + } + mutex.unlock(); + + test_mutex.unlock(); + } +}; + +class rmutex_Thread : public QThread +{ +public: + QMutex mutex; + QWaitCondition cond; + + QMutex &test_mutex; + + inline rmutex_Thread(QMutex &m) : test_mutex(m) { } + + void run() + { + test_mutex.lock(); + test_mutex.lock(); + test_mutex.lock(); + test_mutex.lock(); + + mutex.lock(); + for (int i = 0; i < iterations; ++i) { + cond.wakeOne(); + cond.wait(&mutex); + } + mutex.unlock(); + + test_mutex.unlock(); + test_mutex.unlock(); + test_mutex.unlock(); + test_mutex.unlock(); + } +}; + +void tst_QMutex::lock_unlock_locked_tryLock() +{ + // normal mutex + QMutex mutex; + mutex_Thread thread(mutex); + + QMutex rmutex(QMutex::Recursive); + rmutex_Thread rthread(rmutex); + + for (int i = 0; i < iterations; ++i) { + // normal mutex + QVERIFY(mutex.tryLock()); + mutex.unlock(); + + thread.mutex.lock(); + thread.start(); + + for (int j = 0; j < iterations; ++j) { + QVERIFY(thread.cond.wait(&thread.mutex, 10000)); + QVERIFY(!mutex.tryLock()); + + thread.cond.wakeOne(); + } + + thread.mutex.unlock(); + + QVERIFY(thread.wait(10000)); + QVERIFY(mutex.tryLock()); + + mutex.unlock(); + + // recursive mutex + QVERIFY(rmutex.tryLock()); + QVERIFY(rmutex.tryLock()); + QVERIFY(rmutex.tryLock()); + QVERIFY(rmutex.tryLock()); + + rmutex.unlock(); + rmutex.unlock(); + rmutex.unlock(); + rmutex.unlock(); + + rthread.mutex.lock(); + rthread.start(); + + for (int k = 0; k < iterations; ++k) { + QVERIFY(rthread.cond.wait(&rthread.mutex, 10000)); + QVERIFY(!rmutex.tryLock()); + + rthread.cond.wakeOne(); + } + + rthread.mutex.unlock(); + + QVERIFY(rthread.wait(10000)); + QVERIFY(rmutex.tryLock()); + QVERIFY(rmutex.tryLock()); + QVERIFY(rmutex.tryLock()); + QVERIFY(rmutex.tryLock()); + + rmutex.unlock(); + rmutex.unlock(); + rmutex.unlock(); + rmutex.unlock(); + } +} + +enum { one_minute = 6 * 1000, //not really one minute, but else it is too long. + threadCount = 10 }; + +class StressTestThread : public QThread +{ + QTime t; +public: + static QBasicAtomicInt lockCount; + static QBasicAtomicInt sentinel; + static QMutex mutex; + static int errorCount; + void start() + { + t.start(); + QThread::start(); + } + void run() + { + while (t.elapsed() < one_minute) { + mutex.lock(); + if (sentinel.ref()) ++errorCount; + if (!sentinel.deref()) ++errorCount; + lockCount.ref(); + mutex.unlock(); + if (mutex.tryLock()) { + if (sentinel.ref()) ++errorCount; + if (!sentinel.deref()) ++errorCount; + lockCount.ref(); + mutex.unlock(); + } + } + } +}; +QMutex StressTestThread::mutex; +QBasicAtomicInt StressTestThread::lockCount = Q_BASIC_ATOMIC_INITIALIZER(0); +QBasicAtomicInt StressTestThread::sentinel = Q_BASIC_ATOMIC_INITIALIZER(-1); +int StressTestThread::errorCount = 0; + +void tst_QMutex::stressTest() +{ + StressTestThread threads[threadCount]; + for (int i = 0; i < threadCount; ++i) + threads[i].start(); + QVERIFY(threads[0].wait(one_minute + 10000)); + for (int i = 1; i < threadCount; ++i) + QVERIFY(threads[i].wait(10000)); + QCOMPARE(StressTestThread::errorCount, 0); + qDebug("locked %d times", int(StressTestThread::lockCount)); +} + +class TryLockRaceThread : public QThread +{ +public: + static QMutex mutex; + + void run() + { + QTime t; + t.start(); + do { + if (mutex.tryLock()) + mutex.unlock(); + } while (t.elapsed() < one_minute/2); + } +}; +QMutex TryLockRaceThread::mutex; + +void tst_QMutex::tryLockRace() +{ + // mutex not in use, should be able to lock it + QVERIFY(TryLockRaceThread::mutex.tryLock()); + TryLockRaceThread::mutex.unlock(); + + // try to break tryLock + TryLockRaceThread thread[threadCount]; + for (int i = 0; i < threadCount; ++i) + thread[i].start(); + for (int i = 0; i < threadCount; ++i) + QVERIFY(thread[i].wait()); + + // mutex not in use, should be able to lock it + QVERIFY(TryLockRaceThread::mutex.tryLock()); + TryLockRaceThread::mutex.unlock(); +} + +// Variable that will be protected by the mutex. Volatile so that the +// the optimiser doesn't mess with it based on the increment-then-decrement +// usage pattern. +static volatile int qtbug16115_trylock_counter; +// Counter for how many times the protected variable has an incorrect value. +static int qtbug16115_failure_count = 0; + +void tst_QMutex::qtbug16115_trylock() +{ + //Used to deadlock on unix + struct TrylockThread : QThread { + TrylockThread(QMutex &mut) : mut(mut) {} + QMutex &mut; + void run() { + for (int i = 0; i < 100000; ++i) { + if (mut.tryLock(0)) { + if ((++qtbug16115_trylock_counter) != 1) + ++qtbug16115_failure_count; + if ((--qtbug16115_trylock_counter) != 0) + ++qtbug16115_failure_count; + mut.unlock(); + } + } + } + }; + QMutex mut; + TrylockThread t1(mut); + TrylockThread t2(mut); + TrylockThread t3(mut); + t1.start(); + t2.start(); + t3.start(); + + for (int i = 0; i < 100000; ++i) { + mut.lock(); + if ((++qtbug16115_trylock_counter) != 1) + ++qtbug16115_failure_count; + if ((--qtbug16115_trylock_counter) != 0) + ++qtbug16115_failure_count; + mut.unlock(); + } + t1.wait(); + t2.wait(); + t3.wait(); + QCOMPARE(qtbug16115_failure_count, 0); +} + + +class MoreStressTestThread : public QThread +{ + QTime t; +public: + static QAtomicInt lockCount; + static QAtomicInt sentinel[threadCount]; + static QMutex mutex[threadCount]; + static QAtomicInt errorCount; + void start() + { + t.start(); + QThread::start(); + } + void run() + { + quint64 i = 0; + while (t.elapsed() < one_minute) { + i++; + uint nb = (i * 9 + lockCount * 13) % threadCount; + QMutexLocker locker(&mutex[nb]); + if (sentinel[nb]) errorCount.ref(); + if (sentinel[nb].fetchAndAddRelaxed(5)) errorCount.ref(); + if (!sentinel[nb].testAndSetRelaxed(5, 0)) errorCount.ref(); + if (sentinel[nb]) errorCount.ref(); + lockCount.ref(); + nb = (nb * 17 + i * 5 + lockCount * 3) % threadCount; + if (mutex[nb].tryLock()) { + if (sentinel[nb]) errorCount.ref(); + if (sentinel[nb].fetchAndAddRelaxed(16)) errorCount.ref(); + if (!sentinel[nb].testAndSetRelaxed(16, 0)) errorCount.ref(); + if (sentinel[nb]) errorCount.ref(); + lockCount.ref(); + mutex[nb].unlock(); + } + nb = (nb * 15 + i * 47 + lockCount * 31) % threadCount; + if (mutex[nb].tryLock(2)) { + if (sentinel[nb]) errorCount.ref(); + if (sentinel[nb].fetchAndAddRelaxed(53)) errorCount.ref(); + if (!sentinel[nb].testAndSetRelaxed(53, 0)) errorCount.ref(); + if (sentinel[nb]) errorCount.ref(); + lockCount.ref(); + mutex[nb].unlock(); + } + } + } +}; +QMutex MoreStressTestThread::mutex[threadCount]; +QAtomicInt MoreStressTestThread::lockCount; +QAtomicInt MoreStressTestThread::sentinel[threadCount]; +QAtomicInt MoreStressTestThread::errorCount = 0; + +void tst_QMutex::moreStress() +{ + MoreStressTestThread threads[threadCount]; + for (int i = 0; i < threadCount; ++i) + threads[i].start(); + QVERIFY(threads[0].wait(one_minute + 10000)); + for (int i = 1; i < threadCount; ++i) + QVERIFY(threads[i].wait(10000)); + qDebug("locked %d times", int(MoreStressTestThread::lockCount)); + QCOMPARE(int(MoreStressTestThread::errorCount), 0); +} + + +QTEST_MAIN(tst_QMutex) +#include "tst_qmutex.moc" diff --git a/tests/auto/corelib/thread/qmutexlocker/.gitignore b/tests/auto/corelib/thread/qmutexlocker/.gitignore new file mode 100644 index 0000000000..7c75c1c2f4 --- /dev/null +++ b/tests/auto/corelib/thread/qmutexlocker/.gitignore @@ -0,0 +1 @@ +tst_qmutexlocker diff --git a/tests/auto/corelib/thread/qmutexlocker/qmutexlocker.pro b/tests/auto/corelib/thread/qmutexlocker/qmutexlocker.pro new file mode 100644 index 0000000000..01c369101b --- /dev/null +++ b/tests/auto/corelib/thread/qmutexlocker/qmutexlocker.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +SOURCES += tst_qmutexlocker.cpp +QT = core +CONFIG += parallel_test diff --git a/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp b/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp new file mode 100644 index 0000000000..a663b60be7 --- /dev/null +++ b/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** 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 + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QMutexLockerThread : public QThread +{ +public: + QMutex mutex; + QSemaphore semaphore, testSemaphore; + + void waitForTest() + { + semaphore.release(); + testSemaphore.acquire(); + } + + tst_QMutexLockerThread() + : mutex(QMutex::Recursive) + { + } +}; + +class tst_QMutexLocker : public QObject +{ + Q_OBJECT + +public: + tst_QMutexLocker(); + ~tst_QMutexLocker(); + + tst_QMutexLockerThread *thread; + + void waitForThread() + { + thread->semaphore.acquire(); + } + void releaseThread() + { + thread->testSemaphore.release(); + } + +private slots: + void scopeTest(); + void unlockAndRelockTest(); + void lockerStateTest(); +}; + +tst_QMutexLocker::tst_QMutexLocker() +{ +} + +tst_QMutexLocker::~tst_QMutexLocker() +{ +} + +void tst_QMutexLocker::scopeTest() +{ + class ScopeTestThread : public tst_QMutexLockerThread + { + public: + void run() + { + waitForTest(); + + { + QMutexLocker locker(&mutex); + waitForTest(); + } + + waitForTest(); + } + }; + + thread = new ScopeTestThread; + thread->start(); + + waitForThread(); + // mutex should be unlocked before entering the scope that creates the QMutexLocker + QVERIFY(thread->mutex.tryLock()); + thread->mutex.unlock(); + releaseThread(); + + waitForThread(); + // mutex should be locked by the QMutexLocker + QVERIFY(!thread->mutex.tryLock()); + releaseThread(); + + waitForThread(); + // mutex should be unlocked when the QMutexLocker goes out of scope + QVERIFY(thread->mutex.tryLock()); + thread->mutex.unlock(); + releaseThread(); + + QVERIFY(thread->wait()); + + delete thread; + thread = 0; +} + + +void tst_QMutexLocker::unlockAndRelockTest() +{ + class UnlockAndRelockThread : public tst_QMutexLockerThread + { + public: + void run() + { + QMutexLocker locker(&mutex); + + waitForTest(); + + locker.unlock(); + + waitForTest(); + + locker.relock(); + + waitForTest(); + } + }; + + thread = new UnlockAndRelockThread; + thread->start(); + + waitForThread(); + // mutex should be locked by the QMutexLocker + QVERIFY(!thread->mutex.tryLock()); + releaseThread(); + + waitForThread(); + // mutex has been explicitly unlocked via QMutexLocker + QVERIFY(thread->mutex.tryLock()); + thread->mutex.unlock(); + releaseThread(); + + waitForThread(); + // mutex has been explicitly relocked via QMutexLocker + QVERIFY(!thread->mutex.tryLock()); + releaseThread(); + + QVERIFY(thread->wait()); + + delete thread; + thread = 0; +} + +void tst_QMutexLocker::lockerStateTest() +{ + class LockerStateThread : public tst_QMutexLockerThread + { + public: + void run() + { + { + QMutexLocker locker(&mutex); + locker.relock(); + locker.unlock(); + + waitForTest(); + } + + waitForTest(); + } + }; + + thread = new LockerStateThread; + thread->start(); + + waitForThread(); + // even though we relock() after creating the QMutexLocker, it shouldn't lock the mutex more than once + QVERIFY(thread->mutex.tryLock()); + thread->mutex.unlock(); + releaseThread(); + + waitForThread(); + // if we call QMutexLocker::unlock(), its destructor should do nothing + QVERIFY(thread->mutex.tryLock()); + thread->mutex.unlock(); + releaseThread(); + + QVERIFY(thread->wait()); + + delete thread; + thread = 0; +} + +QTEST_MAIN(tst_QMutexLocker) +#include "tst_qmutexlocker.moc" diff --git a/tests/auto/corelib/thread/qreadlocker/.gitignore b/tests/auto/corelib/thread/qreadlocker/.gitignore new file mode 100644 index 0000000000..2539362670 --- /dev/null +++ b/tests/auto/corelib/thread/qreadlocker/.gitignore @@ -0,0 +1 @@ +tst_qreadlocker diff --git a/tests/auto/corelib/thread/qreadlocker/qreadlocker.pro b/tests/auto/corelib/thread/qreadlocker/qreadlocker.pro new file mode 100644 index 0000000000..ee533070a7 --- /dev/null +++ b/tests/auto/corelib/thread/qreadlocker/qreadlocker.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +SOURCES += tst_qreadlocker.cpp +QT = core +CONFIG += parallel_test diff --git a/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp b/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp new file mode 100644 index 0000000000..3ec9f1cb25 --- /dev/null +++ b/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** 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 + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QReadLockerThread : public QThread +{ +public: + QReadWriteLock lock; + QSemaphore semaphore, testSemaphore; + + void waitForTest() + { + semaphore.release(); + testSemaphore.acquire(); + } +}; + +class tst_QReadLocker : public QObject +{ + Q_OBJECT + +public: + tst_QReadLocker(); + ~tst_QReadLocker(); + + tst_QReadLockerThread *thread; + + void waitForThread() + { + thread->semaphore.acquire(); + } + void releaseThread() + { + thread->testSemaphore.release(); + } + +private slots: + void scopeTest(); + void unlockAndRelockTest(); + void lockerStateTest(); +}; + +tst_QReadLocker::tst_QReadLocker() +{ +} + +tst_QReadLocker::~tst_QReadLocker() +{ +} + +void tst_QReadLocker::scopeTest() +{ + class ScopeTestThread : public tst_QReadLockerThread + { + public: + void run() + { + waitForTest(); + + { + QReadLocker locker(&lock); + waitForTest(); + } + + waitForTest(); + } + }; + + thread = new ScopeTestThread; + thread->start(); + + waitForThread(); + // lock should be unlocked before entering the scope that creates the QReadLocker + QVERIFY(thread->lock.tryLockForWrite()); + thread->lock.unlock(); + releaseThread(); + + waitForThread(); + // lock should be locked by the QReadLocker + QVERIFY(!thread->lock.tryLockForWrite()); + releaseThread(); + + waitForThread(); + // lock should be unlocked when the QReadLocker goes out of scope + QVERIFY(thread->lock.tryLockForWrite()); + thread->lock.unlock(); + releaseThread(); + + QVERIFY(thread->wait()); + + delete thread; + thread = 0; +} + + +void tst_QReadLocker::unlockAndRelockTest() +{ + class UnlockAndRelockThread : public tst_QReadLockerThread + { + public: + void run() + { + QReadLocker locker(&lock); + + waitForTest(); + + locker.unlock(); + + waitForTest(); + + locker.relock(); + + waitForTest(); + } + }; + + thread = new UnlockAndRelockThread; + thread->start(); + + waitForThread(); + // lock should be locked by the QReadLocker + QVERIFY(!thread->lock.tryLockForWrite()); + releaseThread(); + + waitForThread(); + // lock has been explicitly unlocked via QReadLocker + QVERIFY(thread->lock.tryLockForWrite()); + thread->lock.unlock(); + releaseThread(); + + waitForThread(); + // lock has been explicitly relocked via QReadLocker + QVERIFY(!thread->lock.tryLockForWrite()); + releaseThread(); + + QVERIFY(thread->wait()); + + delete thread; + thread = 0; +} + +void tst_QReadLocker::lockerStateTest() +{ + class LockerStateThread : public tst_QReadLockerThread + { + public: + void run() + { + { + QReadLocker locker(&lock); + locker.relock(); + locker.unlock(); + + waitForTest(); + } + + waitForTest(); + } + }; + + thread = new LockerStateThread; + thread->start(); + + waitForThread(); + // even though we relock() after creating the QReadLocker, it shouldn't lock the lock more than once + QVERIFY(thread->lock.tryLockForWrite()); + thread->lock.unlock(); + releaseThread(); + + waitForThread(); + // if we call QReadLocker::unlock(), its destructor should do nothing + QVERIFY(thread->lock.tryLockForWrite()); + thread->lock.unlock(); + releaseThread(); + + QVERIFY(thread->wait()); + + delete thread; + thread = 0; +} + +QTEST_MAIN(tst_QReadLocker) +#include "tst_qreadlocker.moc" diff --git a/tests/auto/corelib/thread/qreadwritelock/.gitignore b/tests/auto/corelib/thread/qreadwritelock/.gitignore new file mode 100644 index 0000000000..96fda685f5 --- /dev/null +++ b/tests/auto/corelib/thread/qreadwritelock/.gitignore @@ -0,0 +1 @@ +tst_qreadwritelock diff --git a/tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro b/tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro new file mode 100644 index 0000000000..93f7c68dc3 --- /dev/null +++ b/tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +SOURCES += tst_qreadwritelock.cpp +QT = core +CONFIG += parallel_test diff --git a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp new file mode 100644 index 0000000000..1b995e8d19 --- /dev/null +++ b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp @@ -0,0 +1,1140 @@ +/**************************************************************************** +** +** 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 +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) +#include +#define sleep(X) Sleep(X) +#endif + +//on solaris, threads that loop one the release bool variable +//needs to sleep more than 1 usec. +#ifdef Q_OS_SOLARIS +# define RWTESTSLEEP usleep(10); +#else +# define RWTESTSLEEP usleep(1); +#endif + +#include + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QReadWriteLock : public QObject +{ + Q_OBJECT +public: + tst_QReadWriteLock(); + virtual ~tst_QReadWriteLock(); + + +/* + Singlethreaded tests +*/ +private slots: +void constructDestruct(); +void readLockUnlock(); +void writeLockUnlock(); +void readLockUnlockLoop(); +void writeLockUnlockLoop(); +void readLockLoop(); +void writeLockLoop(); +void readWriteLockUnlockLoop(); +void tryReadLock(); +void tryWriteLock(); +/* + Multithreaded tests +*/ +private slots: + +void readLockBlockRelease(); +void writeLockBlockRelease(); +void multipleReadersBlockRelease(); +void multipleReadersLoop(); +void multipleWritersLoop(); +void multipleReadersWritersLoop(); +void countingTest(); +void limitedReaders(); +void deleteOnUnlock(); + +/* + Performance tests +*/ +private slots: +void uncontendedLocks(); + + // recursive locking tests + void recursiveReadLock(); + void recursiveWriteLock(); +}; + +tst_QReadWriteLock::tst_QReadWriteLock() +{ + +} + +tst_QReadWriteLock::~tst_QReadWriteLock() +{ + +} + +void tst_QReadWriteLock::constructDestruct() +{ + { + QReadWriteLock rwlock; + } +} + +void tst_QReadWriteLock::readLockUnlock() +{ + QReadWriteLock rwlock; + rwlock.lockForRead(); + rwlock.unlock(); +} + +void tst_QReadWriteLock::writeLockUnlock() +{ + QReadWriteLock rwlock; + rwlock.lockForWrite(); + rwlock.unlock(); +} + +void tst_QReadWriteLock::readLockUnlockLoop() +{ + QReadWriteLock rwlock; + int runs=10000; + int i; + for (i=0; i= 1000); + testsTurn.release(); + + threadsTurn.acquire(); + timer.start(); + QVERIFY(readWriteLock.tryLockForRead(1000)); + QVERIFY(timer.elapsed() <= 1000); + lockCount.ref(); + QVERIFY(readWriteLock.tryLockForRead(1000)); + lockCount.ref(); + lockCount.deref(); + readWriteLock.unlock(); + lockCount.deref(); + readWriteLock.unlock(); + testsTurn.release(); + + threadsTurn.acquire(); + } + }; + + Thread thread; + thread.start(); + + testsTurn.acquire(); + readWriteLock.lockForWrite(); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + threadsTurn.release(); + + testsTurn.acquire(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + readWriteLock.unlock(); + threadsTurn.release(); + + testsTurn.acquire(); + readWriteLock.lockForWrite(); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + threadsTurn.release(); + + testsTurn.acquire(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + readWriteLock.unlock(); + threadsTurn.release(); + + // stop thread + testsTurn.acquire(); + threadsTurn.release(); + thread.wait(); + } +} + +void tst_QReadWriteLock::tryWriteLock() +{ + { + QReadWriteLock rwlock; + QVERIFY(rwlock.tryLockForWrite()); + rwlock.unlock(); + QVERIFY(rwlock.tryLockForWrite()); + rwlock.unlock(); + + rwlock.lockForWrite(); + QVERIFY(!rwlock.tryLockForWrite()); + QVERIFY(!rwlock.tryLockForWrite()); + rwlock.unlock(); + + rwlock.lockForRead(); + QVERIFY(!rwlock.tryLockForWrite()); + rwlock.unlock(); + } + + { + QReadWriteLock rwlock(QReadWriteLock::Recursive); + QVERIFY(rwlock.tryLockForWrite()); + rwlock.unlock(); + QVERIFY(rwlock.tryLockForWrite()); + rwlock.unlock(); + + rwlock.lockForWrite(); + QVERIFY(rwlock.tryLockForWrite()); + QVERIFY(rwlock.tryLockForWrite()); + rwlock.unlock(); + rwlock.unlock(); + rwlock.unlock(); + + rwlock.lockForRead(); + QVERIFY(!rwlock.tryLockForWrite()); + rwlock.unlock(); + } + + // functionality test + { + class Thread : public QThread + { + public: + Thread() : failureCount(0) { } + void run() + { + testsTurn.release(); + + threadsTurn.acquire(); + if (readWriteLock.tryLockForWrite()) + failureCount++; + testsTurn.release(); + + threadsTurn.acquire(); + if (!readWriteLock.tryLockForWrite()) + failureCount++; + if (!lockCount.testAndSetRelaxed(0, 1)) + failureCount++; + if (!lockCount.testAndSetRelaxed(1, 0)) + failureCount++; + readWriteLock.unlock(); + testsTurn.release(); + + threadsTurn.acquire(); + if (readWriteLock.tryLockForWrite(1000)) + failureCount++; + testsTurn.release(); + + threadsTurn.acquire(); + if (!readWriteLock.tryLockForWrite(1000)) + failureCount++; + if (!lockCount.testAndSetRelaxed(0, 1)) + failureCount++; + if (!lockCount.testAndSetRelaxed(1, 0)) + failureCount++; + readWriteLock.unlock(); + testsTurn.release(); + + threadsTurn.acquire(); + } + + int failureCount; + }; + + Thread thread; + thread.start(); + + testsTurn.acquire(); + readWriteLock.lockForRead(); + lockCount.ref(); + threadsTurn.release(); + + testsTurn.acquire(); + lockCount.deref(); + readWriteLock.unlock(); + threadsTurn.release(); + + testsTurn.acquire(); + readWriteLock.lockForRead(); + lockCount.ref(); + threadsTurn.release(); + + testsTurn.acquire(); + lockCount.deref(); + readWriteLock.unlock(); + threadsTurn.release(); + + // stop thread + testsTurn.acquire(); + threadsTurn.release(); + thread.wait(); + + QCOMPARE(thread.failureCount, 0); + } +} + +bool threadDone; +volatile bool release; + +/* + write-lock + unlock + set threadone +*/ +class WriteLockThread : public QThread +{ +public: + QReadWriteLock &testRwlock; + inline WriteLockThread(QReadWriteLock &l) : testRwlock(l) { } + void run() + { + testRwlock.lockForWrite(); + testRwlock.unlock(); + threadDone=true; + } +}; + +/* + read-lock + unlock + set threadone +*/ +class ReadLockThread : public QThread +{ +public: + QReadWriteLock &testRwlock; + inline ReadLockThread(QReadWriteLock &l) : testRwlock(l) { } + void run() + { + testRwlock.lockForRead(); + testRwlock.unlock(); + threadDone=true; + } +}; +/* + write-lock + wait for release==true + unlock +*/ +class WriteLockReleasableThread : public QThread +{ +public: + QReadWriteLock &testRwlock; + inline WriteLockReleasableThread(QReadWriteLock &l) : testRwlock(l) { } + void run() + { + testRwlock.lockForWrite(); + while(release==false) { + RWTESTSLEEP + } + testRwlock.unlock(); + } +}; + +/* + read-lock + wait for release==true + unlock +*/ +class ReadLockReleasableThread : public QThread +{ +public: + QReadWriteLock &testRwlock; + inline ReadLockReleasableThread(QReadWriteLock &l) : testRwlock(l) { } + void run() + { + testRwlock.lockForRead(); + while(release==false) { + RWTESTSLEEP + } + testRwlock.unlock(); + } +}; + + +/* + for(runTime msecs) + read-lock + msleep(holdTime msecs) + release lock + msleep(waitTime msecs) +*/ +class ReadLockLoopThread : public QThread +{ +public: + QReadWriteLock &testRwlock; + int runTime; + int holdTime; + int waitTime; + bool print; + QTime t; + inline ReadLockLoopThread(QReadWriteLock &l, int runTime, int holdTime=0, int waitTime=0, bool print=false) + :testRwlock(l) + ,runTime(runTime) + ,holdTime(holdTime) + ,waitTime(waitTime) + ,print(print) + { } + void run() + { + t.start(); + while (t.elapsed()start(); + for (i=0; iwait(); + for (i=0; istart(); + for (i=0; iwait(); + for (i=0; istart(QThread::NormalPriority); + for (i=0; istart(QThread::IdlePriority); + + for (i=0; iwait(); + for (i=0; iwait(); + + for (i=0; istart(QThread::NormalPriority); + for (i=0; istart(QThread::LowestPriority); + + for (i=0; iwait(); + for (i=0; iwait(); + + for (i=0; ilock(); + m_startup->wakeAll(); + m_waitMutex->unlock(); + + // DeleteOnUnlockThread and the main thread will race from this point + (*m_lock)->lockForWrite(); + (*m_lock)->unlock(); + delete *m_lock; + } +private: + QReadWriteLock **m_lock; + QWaitCondition *m_startup; + QMutex *m_waitMutex; +}; + +void tst_QReadWriteLock::deleteOnUnlock() +{ + QReadWriteLock *lock = 0; + QWaitCondition startup; + QMutex waitMutex; + + DeleteOnUnlockThread thread2(&lock, &startup, &waitMutex); + + QTime t; + t.start(); + while(t.elapsed() < 4000) { + lock = new QReadWriteLock(); + waitMutex.lock(); + lock->lockForWrite(); + thread2.start(); + startup.wait(&waitMutex); + waitMutex.unlock(); + + // DeleteOnUnlockThread and the main thread will race from this point + lock->unlock(); + + thread2.wait(); + } +} + + +void tst_QReadWriteLock::uncontendedLocks() +{ + + uint read=0; + uint write=0; + uint count=0; + int millisecs=1000; + { + QTime t; + t.start(); + while(t.elapsed() tryLockForWrite(); + testsTurn.release(); + } + + // test is releasing recursive write lock + for (int i = 0; i < RecursiveLockCount - 1; ++i) { + threadsTurn.acquire(); + tryLockForWriteResult = lock->tryLockForWrite(); + testsTurn.release(); + } + + // after final unlock in test, we should get the lock + threadsTurn.acquire(); + tryLockForWriteResult = lock->tryLockForWrite(); + testsTurn.release(); + + // cleanup + threadsTurn.acquire(); + lock->unlock(); + testsTurn.release(); + + // test will lockForRead(), then we will lockForWrite() + // (and block), purpose is to ensure that the test can + // recursive lockForRead() even with a waiting writer + threadsTurn.acquire(); + // testsTurn.release(); // ### do not release here, the test uses tryAcquire() + lock->lockForWrite(); + lock->unlock(); + } + }; + + // init + QReadWriteLock lock(QReadWriteLock::Recursive); + RecursiveReadLockThread thread; + thread.lock = &lock; + thread.start(); + + testsTurn.acquire(); + + // verify that we can get multiple read locks in the same thread + for (int i = 0; i < RecursiveLockCount; ++i) { + QVERIFY(lock.tryLockForRead()); + threadsTurn.release(); + + testsTurn.acquire(); + QVERIFY(!thread.tryLockForWriteResult); + } + + // have to unlock the same number of times that we locked + for (int i = 0;i < RecursiveLockCount - 1; ++i) { + lock.unlock(); + threadsTurn.release(); + + testsTurn.acquire(); + QVERIFY(!thread.tryLockForWriteResult); + } + + // after the final unlock, we should be able to get the write lock + lock.unlock(); + threadsTurn.release(); + + testsTurn.acquire(); + QVERIFY(thread.tryLockForWriteResult); + threadsTurn.release(); + + // check that recursive read locking works even when we have a waiting writer + testsTurn.acquire(); + QVERIFY(lock.tryLockForRead()); + threadsTurn.release(); + + testsTurn.tryAcquire(1, 1000); + QVERIFY(lock.tryLockForRead()); + lock.unlock(); + lock.unlock(); + + // cleanup + QVERIFY(thread.wait()); +} + +void tst_QReadWriteLock::recursiveWriteLock() +{ + // thread to attempt locking for reading while the test recursively locks for writing + class RecursiveWriteLockThread : public QThread + { + public: + QReadWriteLock *lock; + bool tryLockForReadResult; + + void run() + { + testsTurn.release(); + + // test is recursively locking for writing + for (int i = 0; i < RecursiveLockCount; ++i) { + threadsTurn.acquire(); + tryLockForReadResult = lock->tryLockForRead(); + testsTurn.release(); + } + + // test is releasing recursive write lock + for (int i = 0; i < RecursiveLockCount - 1; ++i) { + threadsTurn.acquire(); + tryLockForReadResult = lock->tryLockForRead(); + testsTurn.release(); + } + + // after final unlock in test, we should get the lock + threadsTurn.acquire(); + tryLockForReadResult = lock->tryLockForRead(); + testsTurn.release(); + + // cleanup + lock->unlock(); + } + }; + + // init + QReadWriteLock lock(QReadWriteLock::Recursive); + RecursiveWriteLockThread thread; + thread.lock = &lock; + thread.start(); + + testsTurn.acquire(); + + // verify that we can get multiple read locks in the same thread + for (int i = 0; i < RecursiveLockCount; ++i) { + QVERIFY(lock.tryLockForWrite()); + threadsTurn.release(); + + testsTurn.acquire(); + QVERIFY(!thread.tryLockForReadResult); + } + + // have to unlock the same number of times that we locked + for (int i = 0;i < RecursiveLockCount - 1; ++i) { + lock.unlock(); + threadsTurn.release(); + + testsTurn.acquire(); + QVERIFY(!thread.tryLockForReadResult); + } + + // after the final unlock, thread should be able to get the read lock + lock.unlock(); + threadsTurn.release(); + + testsTurn.acquire(); + QVERIFY(thread.tryLockForReadResult); + + // cleanup + QVERIFY(thread.wait()); +} + +QTEST_MAIN(tst_QReadWriteLock) + +#include "tst_qreadwritelock.moc" diff --git a/tests/auto/corelib/thread/qsemaphore/.gitignore b/tests/auto/corelib/thread/qsemaphore/.gitignore new file mode 100644 index 0000000000..fe86486af4 --- /dev/null +++ b/tests/auto/corelib/thread/qsemaphore/.gitignore @@ -0,0 +1 @@ +tst_qsemaphore diff --git a/tests/auto/corelib/thread/qsemaphore/qsemaphore.pro b/tests/auto/corelib/thread/qsemaphore/qsemaphore.pro new file mode 100644 index 0000000000..a75bf443bc --- /dev/null +++ b/tests/auto/corelib/thread/qsemaphore/qsemaphore.pro @@ -0,0 +1,7 @@ +load(qttest_p4) +SOURCES += tst_qsemaphore.cpp +QT = core + + +CONFIG += parallel_test +mac*:CONFIG+=insignificant_test diff --git a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp new file mode 100644 index 0000000000..a2c6bf16b7 --- /dev/null +++ b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp @@ -0,0 +1,450 @@ +/**************************************************************************** +** +** 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 + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QSemaphore : public QObject +{ + Q_OBJECT + +public: + tst_QSemaphore(); + ~tst_QSemaphore(); + +private slots: + void acquire(); + void tryAcquire(); + void tryAcquireWithTimeout_data(); + void tryAcquireWithTimeout(); + void tryAcquireWithTimeoutStarvation(); + void release(); + void available(); + void producerConsumer(); +}; + +static QSemaphore *semaphore = 0; + +tst_QSemaphore::tst_QSemaphore() +{ } + +tst_QSemaphore::~tst_QSemaphore() +{ } + +class ThreadOne : public QThread +{ +public: + ThreadOne() {} + +protected: + void run() + { + int i = 0; + while ( i < 100 ) { + semaphore->acquire(); + i++; + semaphore->release(); + } + } +}; + +class ThreadN : public QThread +{ + int N; + +public: + ThreadN(int n) :N(n) { } + +protected: + void run() + { + int i = 0; + while ( i < 100 ) { + semaphore->acquire(N); + i++; + semaphore->release(N); + } + } +}; + +void tst_QSemaphore::acquire() +{ + { + // old incrementOne() test + QVERIFY(!semaphore); + semaphore = new QSemaphore; + // make some "thing" available + semaphore->release(); + + ThreadOne t1; + ThreadOne t2; + + t1.start(); + t2.start(); + + QVERIFY(t1.wait(4000)); + QVERIFY(t2.wait(4000)); + + delete semaphore; + semaphore = 0; + } + + // old incrementN() test + { + QVERIFY(!semaphore); + semaphore = new QSemaphore; + // make 4 "things" available + semaphore->release(4); + + ThreadN t1(2); + ThreadN t2(3); + + t1.start(); + t2.start(); + + QVERIFY(t1.wait(4000)); + QVERIFY(t2.wait(4000)); + + delete semaphore; + semaphore = 0; + } + + QSemaphore semaphore; + + QCOMPARE(semaphore.available(), 0); + semaphore.release(); + QCOMPARE(semaphore.available(), 1); + semaphore.release(); + QCOMPARE(semaphore.available(), 2); + semaphore.release(10); + QCOMPARE(semaphore.available(), 12); + semaphore.release(10); + QCOMPARE(semaphore.available(), 22); + + semaphore.acquire(); + QCOMPARE(semaphore.available(), 21); + semaphore.acquire(); + QCOMPARE(semaphore.available(), 20); + semaphore.acquire(10); + QCOMPARE(semaphore.available(), 10); + semaphore.acquire(10); + QCOMPARE(semaphore.available(), 0); +} + +void tst_QSemaphore::tryAcquire() +{ + QSemaphore semaphore; + + QCOMPARE(semaphore.available(), 0); + + semaphore.release(); + QCOMPARE(semaphore.available(), 1); + QVERIFY(!semaphore.tryAcquire(2)); + QCOMPARE(semaphore.available(), 1); + + semaphore.release(); + QCOMPARE(semaphore.available(), 2); + QVERIFY(!semaphore.tryAcquire(3)); + QCOMPARE(semaphore.available(), 2); + + semaphore.release(10); + QCOMPARE(semaphore.available(), 12); + QVERIFY(!semaphore.tryAcquire(100)); + QCOMPARE(semaphore.available(), 12); + + semaphore.release(10); + QCOMPARE(semaphore.available(), 22); + QVERIFY(!semaphore.tryAcquire(100)); + QCOMPARE(semaphore.available(), 22); + + QVERIFY(semaphore.tryAcquire()); + QCOMPARE(semaphore.available(), 21); + + QVERIFY(semaphore.tryAcquire()); + QCOMPARE(semaphore.available(), 20); + + QVERIFY(semaphore.tryAcquire(10)); + QCOMPARE(semaphore.available(), 10); + + QVERIFY(semaphore.tryAcquire(10)); + QCOMPARE(semaphore.available(), 0); + + // should not be able to acquire more + QVERIFY(!semaphore.tryAcquire()); + QCOMPARE(semaphore.available(), 0); + + QVERIFY(!semaphore.tryAcquire()); + QCOMPARE(semaphore.available(), 0); + + QVERIFY(!semaphore.tryAcquire(10)); + QCOMPARE(semaphore.available(), 0); + + QVERIFY(!semaphore.tryAcquire(10)); + QCOMPARE(semaphore.available(), 0); +} + +void tst_QSemaphore::tryAcquireWithTimeout_data() +{ + QTest::addColumn("timeout"); + + QTest::newRow("1s") << 1000; + QTest::newRow("10s") << 10000; +} + +void tst_QSemaphore::tryAcquireWithTimeout() +{ + QFETCH(int, timeout); + + QSemaphore semaphore; + QTime time; + +#define QVERIFYGE(a,b) {int e = a; if (a= " << #b << "=" << b; QVERIFY(e>=b);} +#define QVERIFYLE(a,b) {int e = a; if (btryAcquire(amountToConsume, timeout)) + break; + semaphore->release(amountToConsume); + } + } + }; + + QSemaphore semaphore; + semaphore.release(1); + + Thread consumer; + consumer.semaphore = &semaphore; + consumer.amountToConsume = 1; + consumer.timeout = 1000; + + // start the thread and wait for it to start consuming + consumer.start(); + consumer.startup.acquire(); + + // try to consume more than the thread we started is, and provide a longer + // timeout... we should timeout, not wait indefinitely + QVERIFY(!semaphore.tryAcquire(consumer.amountToConsume * 2, consumer.timeout * 2)); + + // the consumer should still be running + QVERIFY(consumer.isRunning() && !consumer.isFinished()); + + // acquire, and wait for smallConsumer to timeout + semaphore.acquire(); + QVERIFY(consumer.wait()); +} + +void tst_QSemaphore::release() +{ DEPENDS_ON("acquire"); } + +void tst_QSemaphore::available() +{ DEPENDS_ON("acquire"); } + +const char alphabet[] = "ACGTH"; +const int AlphabetSize = sizeof(alphabet) - 1; + +const int BufferSize = 4096; // GCD of BufferSize and alphabet size must be 1 +char buffer[BufferSize]; + +#ifndef Q_OS_WINCE +const int ProducerChunkSize = 3; +const int ConsumerChunkSize = 7; +const int Multiplier = 10; +#else +const int ProducerChunkSize = 2; +const int ConsumerChunkSize = 5; +const int Multiplier = 3; +#endif + +// note: the code depends on the fact that DataSize is a multiple of +// ProducerChunkSize, ConsumerChunkSize, and BufferSize +const int DataSize = ProducerChunkSize * ConsumerChunkSize * BufferSize * Multiplier; + +QSemaphore freeSpace(BufferSize); +QSemaphore usedSpace; + +class Producer : public QThread +{ +public: + void run(); +}; + +void Producer::run() +{ + for (int i = 0; i < DataSize; ++i) { + freeSpace.acquire(); + buffer[i % BufferSize] = alphabet[i % AlphabetSize]; + usedSpace.release(); + } + for (int i = 0; i < DataSize; ++i) { + if ((i % ProducerChunkSize) == 0) + freeSpace.acquire(ProducerChunkSize); + buffer[i % BufferSize] = alphabet[i % AlphabetSize]; + if ((i % ProducerChunkSize) == (ProducerChunkSize - 1)) + usedSpace.release(ProducerChunkSize); + } +} + +class Consumer : public QThread +{ +public: + void run(); +}; + +void Consumer::run() +{ + for (int i = 0; i < DataSize; ++i) { + usedSpace.acquire(); + QCOMPARE(buffer[i % BufferSize], alphabet[i % AlphabetSize]); + freeSpace.release(); + } + for (int i = 0; i < DataSize; ++i) { + if ((i % ConsumerChunkSize) == 0) + usedSpace.acquire(ConsumerChunkSize); + QCOMPARE(buffer[i % BufferSize], alphabet[i % AlphabetSize]); + if ((i % ConsumerChunkSize) == (ConsumerChunkSize - 1)) + freeSpace.release(ConsumerChunkSize); + } +} + +void tst_QSemaphore::producerConsumer() +{ + Producer producer; + Consumer consumer; + producer.start(); + consumer.start(); + producer.wait(); + consumer.wait(); +} + +QTEST_MAIN(tst_QSemaphore) +#include "tst_qsemaphore.moc" diff --git a/tests/auto/corelib/thread/qthread/.gitignore b/tests/auto/corelib/thread/qthread/.gitignore new file mode 100644 index 0000000000..4413a75588 --- /dev/null +++ b/tests/auto/corelib/thread/qthread/.gitignore @@ -0,0 +1 @@ +tst_qthread diff --git a/tests/auto/corelib/thread/qthread/qthread.pro b/tests/auto/corelib/thread/qthread/qthread.pro new file mode 100644 index 0000000000..d3b1028034 --- /dev/null +++ b/tests/auto/corelib/thread/qthread/qthread.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +SOURCES += tst_qthread.cpp +QT = core +symbian:LIBS += -llibpthread +CONFIG += parallel_test diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp new file mode 100644 index 0000000000..3c46212c16 --- /dev/null +++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp @@ -0,0 +1,1249 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include + +#ifdef Q_OS_UNIX +#include +#endif +#if defined(Q_OS_WINCE) +#include +#elif defined(Q_OS_WIN) +#include +#include +#endif + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QThread : public QObject +{ + Q_OBJECT + +public: + tst_QThread(); + virtual ~tst_QThread(); + +private slots: + void currentThreadId(); + void currentThread(); + void idealThreadCount(); + void isFinished(); + void isRunning(); + void setPriority(); + void priority(); + void setStackSize(); + void stackSize(); + void exit(); + void start(); + void terminate(); + void quit(); + void wait(); + void started(); + void finished(); + void terminated(); + void run(); + void exec(); + void setTerminationEnabled(); + void sleep(); + void msleep(); + void usleep(); + + void nativeThreadAdoption(); + void adoptedThreadAffinity(); + void adoptedThreadSetPriority(); + void adoptedThreadExit(); + void adoptedThreadExec(); + void adoptedThreadFinished(); + void adoptedThreadExecFinished(); + void adoptMultipleThreads(); + void adoptMultipleThreadsOverlap(); + + void QTBUG13810_exitAndStart(); + void QTBUG15378_exitAndExec(); + + void connectThreadFinishedSignalToObjectDeleteLaterSlot(); + void wait2(); + void wait3_slowDestructor(); + void destroyFinishRace(); + void startFinishRace(); + void startAndQuitCustomEventLoop(); + + void stressTest(); +}; + +enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute }; + +class SignalRecorder : public QObject +{ + Q_OBJECT +public: + QAtomicInt activationCount; + + inline SignalRecorder() + { activationCount = 0; } + + bool wasActivated() + { return activationCount > 0; } + +public slots: + void slot(); +}; + +void SignalRecorder::slot() +{ activationCount.ref(); } + +class Current_Thread : public QThread +{ +public: + Qt::HANDLE id; + QThread *thread; + + void run() + { + id = QThread::currentThreadId(); + thread = QThread::currentThread(); + } +}; + +class Simple_Thread : public QThread +{ +public: + QMutex mutex; + QWaitCondition cond; + + void run() + { + QMutexLocker locker(&mutex); + cond.wakeOne(); + } +}; + +class Exit_Object : public QObject +{ + Q_OBJECT +public: + QThread *thread; + int code; +public slots: + void slot() + { thread->exit(code); } +}; + +class Exit_Thread : public Simple_Thread +{ +public: + Exit_Object *object; + int code; + int result; + + void run() + { + Simple_Thread::run(); + if (object) { + object->thread = this; + object->code = code; + QTimer::singleShot(100, object, SLOT(slot())); + } + result = exec(); + } +}; + +class Terminate_Thread : public Simple_Thread +{ +public: + void run() + { + setTerminationEnabled(false); + { + QMutexLocker locker(&mutex); + cond.wakeOne(); + cond.wait(&mutex, five_minutes); + } + setTerminationEnabled(true); + qFatal("tst_QThread: test case hung"); + } +}; + +class Quit_Object : public QObject +{ + Q_OBJECT +public: + QThread *thread; +public slots: + void slot() + { thread->quit(); } +}; + +class Quit_Thread : public Simple_Thread +{ +public: + Quit_Object *object; + int result; + + void run() + { + Simple_Thread::run(); + if (object) { + object->thread = this; + QTimer::singleShot(100, object, SLOT(slot())); + } + result = exec(); + } +}; + +class Sleep_Thread : public Simple_Thread +{ +public: + enum SleepType { Second, Millisecond, Microsecond }; + + SleepType sleepType; + int interval; + + int elapsed; // result, in *MILLISECONDS* + + void run() + { + QMutexLocker locker(&mutex); + + elapsed = 0; + QTime time; + time.start(); + switch (sleepType) { + case Second: + sleep(interval); + break; + case Millisecond: + msleep(interval); + break; + case Microsecond: + usleep(interval); + break; + } + elapsed = time.elapsed(); + + cond.wakeOne(); + } +}; + +tst_QThread::tst_QThread() + +{ +} + +tst_QThread::~tst_QThread() +{ + +} + +void tst_QThread::currentThreadId() +{ + Current_Thread thread; + thread.id = 0; + thread.thread = 0; + thread.start(); + QVERIFY(thread.wait(five_minutes)); + QVERIFY(thread.id != 0); + QVERIFY(thread.id != QThread::currentThreadId()); +} + +void tst_QThread::currentThread() +{ + QVERIFY(QThread::currentThread() != 0); + QCOMPARE(QThread::currentThread(), thread()); + + Current_Thread thread; + thread.id = 0; + thread.thread = 0; + thread.start(); + QVERIFY(thread.wait(five_minutes)); + QCOMPARE(thread.thread, (QThread *)&thread); +} + +void tst_QThread::idealThreadCount() +{ + QVERIFY(QThread::idealThreadCount() > 0); + qDebug() << "Available cpu cores:" << QThread::idealThreadCount(); +} + +void tst_QThread::isFinished() +{ + Simple_Thread thread; + QVERIFY(!thread.isFinished()); + QMutexLocker locker(&thread.mutex); + thread.start(); + QVERIFY(!thread.isFinished()); + thread.cond.wait(locker.mutex()); + QVERIFY(thread.wait(five_minutes)); + QVERIFY(thread.isFinished()); +} + +void tst_QThread::isRunning() +{ + Simple_Thread thread; + QVERIFY(!thread.isRunning()); + QMutexLocker locker(&thread.mutex); + thread.start(); + QVERIFY(thread.isRunning()); + thread.cond.wait(locker.mutex()); + QVERIFY(thread.wait(five_minutes)); + QVERIFY(!thread.isRunning()); +} + +void tst_QThread::setPriority() +{ + Simple_Thread thread; + + // cannot change the priority, since the thread is not running + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::IdlePriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::LowestPriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::LowPriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::NormalPriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::HighPriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::HighestPriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::TimeCriticalPriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + + QCOMPARE(thread.priority(), QThread::InheritPriority); + QMutexLocker locker(&thread.mutex); + thread.start(); + + // change the priority of a running thread + QCOMPARE(thread.priority(), QThread::InheritPriority); + thread.setPriority(QThread::IdlePriority); + QCOMPARE(thread.priority(), QThread::IdlePriority); + thread.setPriority(QThread::LowestPriority); + QCOMPARE(thread.priority(), QThread::LowestPriority); + thread.setPriority(QThread::LowPriority); + QCOMPARE(thread.priority(), QThread::LowPriority); + thread.setPriority(QThread::NormalPriority); + QCOMPARE(thread.priority(), QThread::NormalPriority); + thread.setPriority(QThread::HighPriority); + QCOMPARE(thread.priority(), QThread::HighPriority); + thread.setPriority(QThread::HighestPriority); + QCOMPARE(thread.priority(), QThread::HighestPriority); + thread.setPriority(QThread::TimeCriticalPriority); + QCOMPARE(thread.priority(), QThread::TimeCriticalPriority); + thread.cond.wait(locker.mutex()); + QVERIFY(thread.wait(five_minutes)); + + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::IdlePriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::LowestPriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::LowPriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::NormalPriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::HighPriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::HighestPriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); + QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running"); + thread.setPriority(QThread::TimeCriticalPriority); + QCOMPARE(thread.priority(), QThread::InheritPriority); +} + +void tst_QThread::priority() +{ DEPENDS_ON("setPriority"); } + +void tst_QThread::setStackSize() +{ + Simple_Thread thread; + QCOMPARE(thread.stackSize(), 0u); + thread.setStackSize(8192u); + QCOMPARE(thread.stackSize(), 8192u); + thread.setStackSize(0u); + QCOMPARE(thread.stackSize(), 0u); +} + +void tst_QThread::stackSize() +{ + DEPENDS_ON("setStackSize"); +} + +void tst_QThread::exit() +{ + Exit_Thread thread; + thread.object = new Exit_Object; + thread.object->moveToThread(&thread); + thread.code = 42; + thread.result = 0; + QVERIFY(!thread.isFinished()); + QVERIFY(!thread.isRunning()); + QMutexLocker locker(&thread.mutex); + thread.start(); + QVERIFY(thread.isRunning()); + QVERIFY(!thread.isFinished()); + thread.cond.wait(locker.mutex()); + QVERIFY(thread.wait(five_minutes)); + QVERIFY(thread.isFinished()); + QVERIFY(!thread.isRunning()); + QCOMPARE(thread.result, thread.code); + delete thread.object; + + Exit_Thread thread2; + thread2.object = 0; + thread2.code = 53; + thread2.result = 0; + QMutexLocker locker2(&thread2.mutex); + thread2.start(); + thread2.exit(thread2.code); + thread2.cond.wait(locker2.mutex()); + QVERIFY(thread2.wait(five_minutes)); + QCOMPARE(thread2.result, thread2.code); +} + +void tst_QThread::start() +{ + QThread::Priority priorities[] = { + QThread::IdlePriority, + QThread::LowestPriority, + QThread::LowPriority, + QThread::NormalPriority, + QThread::HighPriority, + QThread::HighestPriority, + QThread::TimeCriticalPriority, + QThread::InheritPriority + }; + const int prio_count = sizeof(priorities) / sizeof(QThread::Priority); + + for (int i = 0; i < prio_count; ++i) { + Simple_Thread thread; + QVERIFY(!thread.isFinished()); + QVERIFY(!thread.isRunning()); + QMutexLocker locker(&thread.mutex); + thread.start(priorities[i]); + QVERIFY(thread.isRunning()); + QVERIFY(!thread.isFinished()); + thread.cond.wait(locker.mutex()); + QVERIFY(thread.wait(five_minutes)); + QVERIFY(thread.isFinished()); + QVERIFY(!thread.isRunning()); + } +} + +void tst_QThread::terminate() +{ + Terminate_Thread thread; + { + QMutexLocker locker(&thread.mutex); + thread.start(); + QVERIFY(thread.cond.wait(locker.mutex(), five_minutes)); + thread.terminate(); + thread.cond.wakeOne(); + } + QVERIFY(thread.wait(five_minutes)); +} + +void tst_QThread::quit() +{ + Quit_Thread thread; + thread.object = new Quit_Object; + thread.object->moveToThread(&thread); + thread.result = -1; + QVERIFY(!thread.isFinished()); + QVERIFY(!thread.isRunning()); + QMutexLocker locker(&thread.mutex); + thread.start(); + QVERIFY(thread.isRunning()); + QVERIFY(!thread.isFinished()); + thread.cond.wait(locker.mutex()); + QVERIFY(thread.wait(five_minutes)); + QVERIFY(thread.isFinished()); + QVERIFY(!thread.isRunning()); + QCOMPARE(thread.result, 0); + delete thread.object; + + Quit_Thread thread2; + thread2.object = 0; + thread2.result = -1; + QMutexLocker locker2(&thread2.mutex); + thread2.start(); + thread2.quit(); + thread2.cond.wait(locker2.mutex()); + QVERIFY(thread2.wait(five_minutes)); + QCOMPARE(thread2.result, 0); +} + +void tst_QThread::wait() +{ + DEPENDS_ON("isRunning"); + DEPENDS_ON("isFinished"); +} + +void tst_QThread::started() +{ + SignalRecorder recorder; + Simple_Thread thread; + connect(&thread, SIGNAL(started()), &recorder, SLOT(slot()), Qt::DirectConnection); + thread.start(); + QVERIFY(thread.wait(five_minutes)); + QVERIFY(recorder.wasActivated()); +} + +void tst_QThread::finished() +{ + SignalRecorder recorder; + Simple_Thread thread; + connect(&thread, SIGNAL(finished()), &recorder, SLOT(slot()), Qt::DirectConnection); + thread.start(); + QVERIFY(thread.wait(five_minutes)); + QVERIFY(recorder.wasActivated()); +} + +void tst_QThread::terminated() +{ + SignalRecorder recorder; + Terminate_Thread thread; + connect(&thread, SIGNAL(terminated()), &recorder, SLOT(slot()), Qt::DirectConnection); + { + QMutexLocker locker(&thread.mutex); + thread.start(); + thread.cond.wait(locker.mutex()); + thread.terminate(); + thread.cond.wakeOne(); + } + QVERIFY(thread.wait(five_minutes)); + QVERIFY(recorder.wasActivated()); +} + +void tst_QThread::run() +{ DEPENDS_ON("wait()"); } + +void tst_QThread::exec() +{ + DEPENDS_ON("exit()"); + DEPENDS_ON("quit()"); + + class MultipleExecThread : public QThread + { + public: + int res1, res2; + + MultipleExecThread() : res1(-2), res2(-2) { } + + void run() + { + { + Exit_Object o; + o.thread = this; + o.code = 1; + QTimer::singleShot(100, &o, SLOT(slot())); + res1 = exec(); + } + { + Exit_Object o; + o.thread = this; + o.code = 2; + QTimer::singleShot(100, &o, SLOT(slot())); + res2 = exec(); + } + } + }; + + MultipleExecThread thread; + thread.start(); + QVERIFY(thread.wait()); + + QCOMPARE(thread.res1, 1); + QCOMPARE(thread.res2, 2); +} + +void tst_QThread::setTerminationEnabled() +{ DEPENDS_ON("terminate"); } + +void tst_QThread::sleep() +{ + Sleep_Thread thread; + thread.sleepType = Sleep_Thread::Second; + thread.interval = 2; + thread.start(); + QVERIFY(thread.wait(five_minutes)); + QVERIFY(thread.elapsed >= 2000); +} + +void tst_QThread::msleep() +{ + Sleep_Thread thread; + thread.sleepType = Sleep_Thread::Millisecond; + thread.interval = 120; + thread.start(); + QVERIFY(thread.wait(five_minutes)); +#if defined (Q_OS_WIN) + // Since the resolution of QTime is so coarse... + QVERIFY(thread.elapsed >= 100); +#else + QVERIFY(thread.elapsed >= 120); +#endif +} + +void tst_QThread::usleep() +{ + Sleep_Thread thread; + thread.sleepType = Sleep_Thread::Microsecond; + thread.interval = 120000; + thread.start(); + QVERIFY(thread.wait(five_minutes)); +#if defined (Q_OS_WIN) + // Since the resolution of QTime is so coarse... + QVERIFY(thread.elapsed >= 100); +#else + QVERIFY(thread.elapsed >= 120); +#endif +} + +typedef void (*FunctionPointer)(void *); +void noop(void*) { } + +#ifdef Q_OS_SYMBIAN +typedef RThread ThreadHandle; +#elif defined Q_OS_UNIX + typedef pthread_t ThreadHandle; +#elif defined Q_OS_WIN + typedef HANDLE ThreadHandle; +#endif + +#ifdef Q_OS_WIN +#define WIN_FIX_STDCALL __stdcall +#else +#define WIN_FIX_STDCALL +#endif + +class NativeThreadWrapper +{ +public: + NativeThreadWrapper() : qthread(0), waitForStop(false) {} + void start(FunctionPointer functionPointer = noop, void *data = 0); + void startAndWait(FunctionPointer functionPointer = noop, void *data = 0); + void join(); + void setWaitForStop() { waitForStop = true; } + void stop(); + + ThreadHandle nativeThreadHandle; + QThread *qthread; + QWaitCondition startCondition; + QMutex mutex; + bool waitForStop; + QWaitCondition stopCondition; +protected: + static void *runUnix(void *data); + static unsigned WIN_FIX_STDCALL runWin(void *data); + static int runSymbian(void *data); + + FunctionPointer functionPointer; + void *data; +}; + +void NativeThreadWrapper::start(FunctionPointer functionPointer, void *data) +{ + this->functionPointer = functionPointer; + this->data = data; +#ifdef Q_OS_SYMBIAN + qt_symbian_throwIfError(nativeThreadHandle.Create(KNullDesC(), NativeThreadWrapper::runSymbian, 1024, &User::Allocator(), this)); + nativeThreadHandle.Resume(); +#elif defined Q_OS_UNIX + const int state = pthread_create(&nativeThreadHandle, 0, NativeThreadWrapper::runUnix, this); + Q_UNUSED(state); +#elif defined(Q_OS_WINCE) + nativeThreadHandle = CreateThread(NULL, 0 , (LPTHREAD_START_ROUTINE)NativeThreadWrapper::runWin , this, 0, NULL); +#elif defined Q_OS_WIN + unsigned thrdid = 0; + nativeThreadHandle = (Qt::HANDLE) _beginthreadex(NULL, 0, NativeThreadWrapper::runWin, this, 0, &thrdid); +#endif +} + +void NativeThreadWrapper::startAndWait(FunctionPointer functionPointer, void *data) +{ + QMutexLocker locker(&mutex); + start(functionPointer, data); + startCondition.wait(locker.mutex()); +} + +void NativeThreadWrapper::join() +{ +#ifdef Q_OS_SYMBIAN + TRequestStatus stat; + nativeThreadHandle.Logon(stat); + User::WaitForRequest(stat); + nativeThreadHandle.Close(); +#elif defined Q_OS_UNIX + pthread_join(nativeThreadHandle, 0); +#elif defined Q_OS_WIN + WaitForSingleObject(nativeThreadHandle, INFINITE); + CloseHandle(nativeThreadHandle); +#endif +} + +void *NativeThreadWrapper::runUnix(void *that) +{ + NativeThreadWrapper *nativeThreadWrapper = reinterpret_cast(that); + + // Adopt thread, create QThread object. + nativeThreadWrapper->qthread = QThread::currentThread(); + + // Release main thread. + { + QMutexLocker lock(&nativeThreadWrapper->mutex); + nativeThreadWrapper->startCondition.wakeOne(); + } + + // Run function. + nativeThreadWrapper->functionPointer(nativeThreadWrapper->data); + + // Wait for stop. + { + QMutexLocker lock(&nativeThreadWrapper->mutex); + if (nativeThreadWrapper->waitForStop) + nativeThreadWrapper->stopCondition.wait(lock.mutex()); + } + + return 0; +} + +unsigned WIN_FIX_STDCALL NativeThreadWrapper::runWin(void *data) +{ + runUnix(data); + return 0; +} + +int NativeThreadWrapper::runSymbian(void *data) +{ + runUnix(data); + return 0; +} + +void NativeThreadWrapper::stop() +{ + QMutexLocker lock(&mutex); + waitForStop = false; + stopCondition.wakeOne(); +} + +bool threadAdoptedOk = false; +QThread *mainThread; +void testNativeThreadAdoption(void *) +{ + threadAdoptedOk = (QThread::currentThreadId() != 0 + && QThread::currentThread() != 0 + && QThread::currentThread() != mainThread); +} +void tst_QThread::nativeThreadAdoption() +{ + threadAdoptedOk = false; + mainThread = QThread::currentThread(); + NativeThreadWrapper nativeThread; + nativeThread.setWaitForStop(); + nativeThread.startAndWait(testNativeThreadAdoption); + QVERIFY(nativeThread.qthread); + + nativeThread.stop(); + nativeThread.join(); + + QVERIFY(threadAdoptedOk); +} + +void adoptedThreadAffinityFunction(void *arg) +{ + QThread **affinity = reinterpret_cast(arg); + QThread *current = QThread::currentThread(); + affinity[0] = current; + affinity[1] = current->thread(); +} + +void tst_QThread::adoptedThreadAffinity() +{ + QThread *affinity[2] = { 0, 0 }; + + NativeThreadWrapper thread; + thread.startAndWait(adoptedThreadAffinityFunction, affinity); + thread.join(); + + // adopted thread should have affinity to itself + QCOMPARE(affinity[0], affinity[1]); +} + +void tst_QThread::adoptedThreadSetPriority() +{ + + NativeThreadWrapper nativeThread; + nativeThread.setWaitForStop(); + nativeThread.startAndWait(); + + // change the priority of a running thread + QCOMPARE(nativeThread.qthread->priority(), QThread::InheritPriority); + nativeThread.qthread->setPriority(QThread::IdlePriority); + QCOMPARE(nativeThread.qthread->priority(), QThread::IdlePriority); + nativeThread.qthread->setPriority(QThread::LowestPriority); + QCOMPARE(nativeThread.qthread->priority(), QThread::LowestPriority); + nativeThread.qthread->setPriority(QThread::LowPriority); + QCOMPARE(nativeThread.qthread->priority(), QThread::LowPriority); + nativeThread.qthread->setPriority(QThread::NormalPriority); + QCOMPARE(nativeThread.qthread->priority(), QThread::NormalPriority); + nativeThread.qthread->setPriority(QThread::HighPriority); + QCOMPARE(nativeThread.qthread->priority(), QThread::HighPriority); + nativeThread.qthread->setPriority(QThread::HighestPriority); + QCOMPARE(nativeThread.qthread->priority(), QThread::HighestPriority); + nativeThread.qthread->setPriority(QThread::TimeCriticalPriority); + QCOMPARE(nativeThread.qthread->priority(), QThread::TimeCriticalPriority); + + nativeThread.stop(); + nativeThread.join(); +} + +void tst_QThread::adoptedThreadExit() +{ + NativeThreadWrapper nativeThread; + nativeThread.setWaitForStop(); + + nativeThread.startAndWait(); + QVERIFY(nativeThread.qthread); + QVERIFY(nativeThread.qthread->isRunning()); + QVERIFY(!nativeThread.qthread->isFinished()); + + nativeThread.stop(); + nativeThread.join(); +} + +void adoptedThreadExecFunction(void *) +{ + QThread * const adoptedThread = QThread::currentThread(); + QEventLoop eventLoop(adoptedThread); + + const int code = 1; + Exit_Object o; + o.thread = adoptedThread; + o.code = code; + QTimer::singleShot(100, &o, SLOT(slot())); + + const int result = eventLoop.exec(); + QCOMPARE(result, code); +} + +void tst_QThread::adoptedThreadExec() +{ + NativeThreadWrapper nativeThread; + nativeThread.start(adoptedThreadExecFunction); + nativeThread.join(); +} + +/* + Test that you get the finished signal when an adopted thread exits. +*/ +void tst_QThread::adoptedThreadFinished() +{ + NativeThreadWrapper nativeThread; + nativeThread.setWaitForStop(); + nativeThread.startAndWait(); + + QObject::connect(nativeThread.qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + nativeThread.stop(); + nativeThread.join(); + + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); +} + +void tst_QThread::adoptedThreadExecFinished() +{ + NativeThreadWrapper nativeThread; + nativeThread.setWaitForStop(); + nativeThread.startAndWait(adoptedThreadExecFunction); + + QObject::connect(nativeThread.qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + nativeThread.stop(); + nativeThread.join(); + + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); +} + +void tst_QThread::adoptMultipleThreads() +{ +#if defined(Q_OS_WIN) + // Windows CE is not capable of handling that many threads. On the emulator it is dead with 26 threads already. +# if defined(Q_OS_WINCE) + const int numThreads = 20; +# else + // need to test lots of threads, so that we exceed MAXIMUM_WAIT_OBJECTS in qt_adopted_thread_watcher() + const int numThreads = 200; +# endif +#else + const int numThreads = 5; +#endif + QVector nativeThreads; + + SignalRecorder recorder; + + for (int i = 0; i < numThreads; ++i) { + nativeThreads.append(new NativeThreadWrapper()); + nativeThreads.at(i)->setWaitForStop(); + nativeThreads.at(i)->startAndWait(); + QObject::connect(nativeThreads.at(i)->qthread, SIGNAL(finished()), &recorder, SLOT(slot())); + } + + QObject::connect(nativeThreads.at(numThreads - 1)->qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + for (int i = 0; i < numThreads; ++i) { + nativeThreads.at(i)->stop(); + nativeThreads.at(i)->join(); + delete nativeThreads.at(i); + } + + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QCOMPARE(int(recorder.activationCount), numThreads); +} + +void tst_QThread::adoptMultipleThreadsOverlap() +{ +#if defined(Q_OS_WIN) + // Windows CE is not capable of handling that many threads. On the emulator it is dead with 26 threads already. +# if defined(Q_OS_WINCE) + const int numThreads = 20; +# else + // need to test lots of threads, so that we exceed MAXIMUM_WAIT_OBJECTS in qt_adopted_thread_watcher() + const int numThreads = 200; +# endif +#elif defined(Q_OS_SYMBIAN) + // stress the monitoring thread's add function + const int numThreads = 100; +#else + const int numThreads = 5; +#endif + QVector nativeThreads; + + SignalRecorder recorder; + + for (int i = 0; i < numThreads; ++i) { + nativeThreads.append(new NativeThreadWrapper()); + nativeThreads.at(i)->setWaitForStop(); + nativeThreads.at(i)->mutex.lock(); + nativeThreads.at(i)->start(); + } + for (int i = 0; i < numThreads; ++i) { + nativeThreads.at(i)->startCondition.wait(&nativeThreads.at(i)->mutex); + QObject::connect(nativeThreads.at(i)->qthread, SIGNAL(finished()), &recorder, SLOT(slot())); + nativeThreads.at(i)->mutex.unlock(); + } + + QObject::connect(nativeThreads.at(numThreads - 1)->qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + for (int i = 0; i < numThreads; ++i) { + nativeThreads.at(i)->stop(); + nativeThreads.at(i)->join(); + delete nativeThreads.at(i); + } + + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QCOMPARE(int(recorder.activationCount), numThreads); +} +void tst_QThread::stressTest() +{ +#if defined(Q_OS_WINCE) + QSKIP("Disconnects on WinCE, skipping...", SkipAll); +#endif + QTime t; + t.start(); + while (t.elapsed() < one_minute) { + Current_Thread t; + t.start(); + t.wait(one_minute); + } +} + +class Syncronizer : public QObject +{ Q_OBJECT +public slots: + void setProp(int p) { + if(m_prop != p) { + m_prop = p; + emit propChanged(p); + } + } +signals: + void propChanged(int); +public: + Syncronizer() : m_prop(42) {} + int m_prop; +}; + +void tst_QThread::QTBUG13810_exitAndStart() +{ + QThread thread; + thread.exit(555); //should do nothing + + thread.start(); + + //test that the thread is running by executing queued connected signal there + Syncronizer sync1; + sync1.moveToThread(&thread); + Syncronizer sync2; + sync2.moveToThread(&thread); + connect(&sync2, SIGNAL(propChanged(int)), &sync1, SLOT(setProp(int)), Qt::QueuedConnection); + connect(&sync1, SIGNAL(propChanged(int)), &thread, SLOT(quit()), Qt::QueuedConnection); + QMetaObject::invokeMethod(&sync2, "setProp", Qt::QueuedConnection , Q_ARG(int, 89)); + QTest::qWait(50); + while(!thread.wait(10)) + QTest::qWait(10); + QCOMPARE(sync2.m_prop, 89); + QCOMPARE(sync1.m_prop, 89); +} + +void tst_QThread::QTBUG15378_exitAndExec() +{ + class Thread : public QThread { + public: + QSemaphore sem1; + QSemaphore sem2; + volatile int value; + void run() { + sem1.acquire(); + value = exec(); //First entrence + sem2.release(); + value = exec(); // Second loop + } + }; + Thread thread; + thread.value = 0; + thread.start(); + thread.exit(556); + thread.sem1.release(); //should exit the first loop + thread.sem2.acquire(); + int v = thread.value; + QCOMPARE(v, 556); + + //test that the thread is running by executing queued connected signal there + Syncronizer sync1; + sync1.moveToThread(&thread); + Syncronizer sync2; + sync2.moveToThread(&thread); + connect(&sync2, SIGNAL(propChanged(int)), &sync1, SLOT(setProp(int)), Qt::QueuedConnection); + connect(&sync1, SIGNAL(propChanged(int)), &thread, SLOT(quit()), Qt::QueuedConnection); + QMetaObject::invokeMethod(&sync2, "setProp", Qt::QueuedConnection , Q_ARG(int, 89)); + QTest::qWait(50); + while(!thread.wait(10)) + QTest::qWait(10); + QCOMPARE(sync2.m_prop, 89); + QCOMPARE(sync1.m_prop, 89); +} + +void tst_QThread::connectThreadFinishedSignalToObjectDeleteLaterSlot() +{ + QThread thread; + QObject *object = new QObject; + QWeakPointer p = object; + QVERIFY(!p.isNull()); + connect(&thread, SIGNAL(started()), &thread, SLOT(quit()), Qt::DirectConnection); + connect(&thread, SIGNAL(finished()), object, SLOT(deleteLater())); + object->moveToThread(&thread); + thread.start(); + QVERIFY(thread.wait(30000)); + QVERIFY(p.isNull()); +} + +class Waiting_Thread : public QThread +{ +public: + enum { WaitTime = 800 }; + QMutex mutex; + QWaitCondition cond1; + QWaitCondition cond2; + + void run() + { + QMutexLocker locker(&mutex); + cond1.wait(&mutex); + cond2.wait(&mutex, WaitTime); + } +}; + +void tst_QThread::wait2() +{ + QElapsedTimer timer; + Waiting_Thread thread; + thread.start(); + timer.start(); + QVERIFY(!thread.wait(Waiting_Thread::WaitTime)); + qint64 elapsed = timer.elapsed(); + + QVERIFY(elapsed >= Waiting_Thread::WaitTime); + //QVERIFY(elapsed < Waiting_Thread::WaitTime * 1.4); + + timer.start(); + thread.cond1.wakeOne(); + QVERIFY(thread.wait(/*Waiting_Thread::WaitTime * 1.4*/)); + elapsed = timer.elapsed(); + QVERIFY(elapsed >= Waiting_Thread::WaitTime); + //QVERIFY(elapsed < Waiting_Thread::WaitTime * 1.4); +} + + +class SlowSlotObject : public QObject { + Q_OBJECT +public: + QMutex mutex; + QWaitCondition cond; +public slots: + void slowSlot() { + QMutexLocker locker(&mutex); + cond.wait(&mutex); + } +}; + +void tst_QThread::wait3_slowDestructor() +{ + SlowSlotObject slow; + QThread thread; + QObject::connect(&thread, SIGNAL(finished()), &slow, SLOT(slowSlot()), Qt::DirectConnection); + + enum { WaitTime = 1800 }; + QElapsedTimer timer; + + thread.start(); + thread.quit(); + //the quit function will cause the thread to finish and enter the slowSlot that is blocking + + timer.start(); + QVERIFY(!thread.wait(Waiting_Thread::WaitTime)); + qint64 elapsed = timer.elapsed(); + + QVERIFY(elapsed >= Waiting_Thread::WaitTime); + //QVERIFY(elapsed < Waiting_Thread::WaitTime * 1.4); + + slow.cond.wakeOne(); + //now the thread should finish quickly + QVERIFY(thread.wait(one_minute)); +} + +void tst_QThread::destroyFinishRace() +{ + class Thread : public QThread { void run() {} }; + for (int i = 0; i < 15; i++) { + Thread *thr = new Thread; + connect(thr, SIGNAL(finished()), thr, SLOT(deleteLater())); + QWeakPointer weak(static_cast(thr)); + thr->start(); + while (weak) { + qApp->processEvents(); + qApp->processEvents(); + qApp->processEvents(); + qApp->processEvents(); + } + } +} + +void tst_QThread::startFinishRace() +{ + class Thread : public QThread { + public: + Thread() : i (50) {} + void run() { + i--; + if (!i) disconnect(this, SIGNAL(finished()), 0, 0); + } + int i; + }; + for (int i = 0; i < 15; i++) { + Thread thr; + connect(&thr, SIGNAL(finished()), &thr, SLOT(start())); + thr.start(); + while (!thr.isFinished() || thr.i != 0) { + qApp->processEvents(); + qApp->processEvents(); + qApp->processEvents(); + qApp->processEvents(); + } + QCOMPARE(thr.i, 0); + } +} + +void tst_QThread::startAndQuitCustomEventLoop() +{ + struct Thread : QThread { + void run() { QEventLoop().exec(); } + }; + + for (int i = 0; i < 5; i++) { + Thread t; + t.start(); + t.quit(); + t.wait(); + } +} + + +QTEST_MAIN(tst_QThread) +#include "tst_qthread.moc" 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" 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 diff --git a/tests/auto/corelib/thread/qwaitcondition/.gitignore b/tests/auto/corelib/thread/qwaitcondition/.gitignore new file mode 100644 index 0000000000..96531988d9 --- /dev/null +++ b/tests/auto/corelib/thread/qwaitcondition/.gitignore @@ -0,0 +1 @@ +tst_qwaitcondition diff --git a/tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro b/tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro new file mode 100644 index 0000000000..9af0c71301 --- /dev/null +++ b/tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +SOURCES += tst_qwaitcondition.cpp +QT = core + + +CONFIG += parallel_test diff --git a/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp b/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp new file mode 100644 index 0000000000..6545df9189 --- /dev/null +++ b/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp @@ -0,0 +1,845 @@ +/**************************************************************************** +** +** 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 + +#if defined(Q_OS_SYMBIAN) +// Symbian Open C has a bug that causes very short waits to fail sometimes +#define COND_WAIT_TIME 50 +#else +#define COND_WAIT_TIME 1 +#endif + + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QWaitCondition : public QObject +{ + Q_OBJECT + +public: + tst_QWaitCondition(); + +private slots: + void wait_QMutex(); + void wait_QReadWriteLock(); + void wakeOne(); + void wakeAll(); + void wait_RaceCondition(); +}; + +static const int iterations = 10; + +// Note: some tests rely on ThreadCount being multiple of 2 +#if defined(Q_OS_SOLARIS) || ( defined(Q_OS_LINUX) && defined(QT_ARCH_ARMV6) ) +static const int ThreadCount = 4; +#else +static const int ThreadCount = 10; +#endif + +tst_QWaitCondition::tst_QWaitCondition() + +{ +} + +class wait_QMutex_Thread_1 : public QThread +{ +public: + QMutex mutex; + QWaitCondition cond; + + inline wait_QMutex_Thread_1() + { } + + void run() + { + mutex.lock(); + cond.wakeOne(); + cond.wait(&mutex); + mutex.unlock(); + } +}; + +class wait_QMutex_Thread_2 : public QThread +{ +public: + QWaitCondition started; + + QMutex *mutex; + QWaitCondition *cond; + + inline wait_QMutex_Thread_2() + : mutex(0), cond(0) + { } + + void run() + { + mutex->lock(); + started.wakeOne(); + cond->wait(mutex); + mutex->unlock(); + } +}; + +class wait_QReadWriteLock_Thread_1 : public QThread +{ +public: + QReadWriteLock readWriteLock; + QWaitCondition cond; + + inline wait_QReadWriteLock_Thread_1() + { } + + void run() + { + readWriteLock.lockForWrite(); + cond.wakeOne(); + cond.wait(&readWriteLock); + readWriteLock.unlock(); + } +}; + +class wait_QReadWriteLock_Thread_2 : public QThread +{ +public: + QWaitCondition started; + + QReadWriteLock *readWriteLock; + QWaitCondition *cond; + + inline wait_QReadWriteLock_Thread_2() + : readWriteLock(0), cond(0) + { } + + void run() + { + readWriteLock->lockForRead(); + started.wakeOne(); + cond->wait(readWriteLock); + readWriteLock->unlock(); + } +}; + +void tst_QWaitCondition::wait_QMutex() +{ + int x; + for (int i = 0; i < iterations; ++i) { + { + QMutex mutex; + QWaitCondition cond; + + mutex.lock(); + + cond.wakeOne(); + QVERIFY(!cond.wait(&mutex, 1)); + + cond.wakeAll(); + QVERIFY(!cond.wait(&mutex, 1)); + + mutex.unlock(); + } + + { + // test multiple threads waiting on separate wait conditions + wait_QMutex_Thread_1 thread[ThreadCount]; + + for (x = 0; x < ThreadCount; ++x) { + thread[x].mutex.lock(); + thread[x].start(); + // wait for thread to start + QVERIFY(thread[x].cond.wait(&thread[x].mutex, 1000)); + thread[x].mutex.unlock(); + } + + for (x = 0; x < ThreadCount; ++x) { + QVERIFY(thread[x].isRunning()); + QVERIFY(!thread[x].isFinished()); + } + + for (x = 0; x < ThreadCount; ++x) { + thread[x].mutex.lock(); + thread[x].cond.wakeOne(); + thread[x].mutex.unlock(); + } + + for (x = 0; x < ThreadCount; ++x) { + QVERIFY(thread[x].wait(1000)); + } + } + + { + // test multiple threads waiting on a wait condition + QMutex mutex; + QWaitCondition cond1, cond2; + wait_QMutex_Thread_2 thread[ThreadCount]; + + mutex.lock(); + for (x = 0; x < ThreadCount; ++x) { + thread[x].mutex = &mutex; + thread[x].cond = (x < ThreadCount / 2) ? &cond1 : &cond2; + thread[x].start(); + // wait for thread to start + QVERIFY(thread[x].started.wait(&mutex, 1000)); + } + mutex.unlock(); + + for (x = 0; x < ThreadCount; ++x) { + QVERIFY(thread[x].isRunning()); + QVERIFY(!thread[x].isFinished()); + } + + mutex.lock(); + cond1.wakeAll(); + cond2.wakeAll(); + mutex.unlock(); + + for (x = 0; x < ThreadCount; ++x) { + QVERIFY(thread[x].wait(1000)); + } + } + } +} + +void tst_QWaitCondition::wait_QReadWriteLock() +{ + { + QReadWriteLock readWriteLock(QReadWriteLock::Recursive); + QWaitCondition waitCondition; + + // ensure that the lockForRead is correctly restored + readWriteLock.lockForRead(); + + QVERIFY(!waitCondition.wait(&readWriteLock, 1)); + + QVERIFY(!readWriteLock.tryLockForWrite()); + QVERIFY(readWriteLock.tryLockForRead()); + readWriteLock.unlock(); + QVERIFY(!readWriteLock.tryLockForWrite()); + readWriteLock.unlock(); + + QVERIFY(readWriteLock.tryLockForWrite()); + readWriteLock.unlock(); + } + + { + QReadWriteLock readWriteLock(QReadWriteLock::Recursive); + QWaitCondition waitCondition; + + // ensure that the lockForWrite is correctly restored + readWriteLock.lockForWrite(); + + QVERIFY(!waitCondition.wait(&readWriteLock, 1)); + + QVERIFY(!readWriteLock.tryLockForRead()); + QVERIFY(readWriteLock.tryLockForWrite()); + readWriteLock.unlock(); + QVERIFY(!readWriteLock.tryLockForRead()); + readWriteLock.unlock(); + + QVERIFY(readWriteLock.tryLockForRead()); + readWriteLock.unlock(); + } + + + int x; + for (int i = 0; i < iterations; ++i) { + { + QReadWriteLock readWriteLock; + QWaitCondition waitCondition; + + readWriteLock.lockForRead(); + + waitCondition.wakeOne(); + QVERIFY(!waitCondition.wait(&readWriteLock, 1)); + + waitCondition.wakeAll(); + QVERIFY(!waitCondition.wait(&readWriteLock, 1)); + + readWriteLock.unlock(); + } + + { + QReadWriteLock readWriteLock; + QWaitCondition waitCondition; + + readWriteLock.lockForWrite(); + + waitCondition.wakeOne(); + QVERIFY(!waitCondition.wait(&readWriteLock, 1)); + + waitCondition.wakeAll(); + QVERIFY(!waitCondition.wait(&readWriteLock, 1)); + + readWriteLock.unlock(); + } + + { + // test multiple threads waiting on separate wait conditions + wait_QReadWriteLock_Thread_1 thread[ThreadCount]; + + for (x = 0; x < ThreadCount; ++x) { + thread[x].readWriteLock.lockForRead(); + thread[x].start(); + // wait for thread to start +#if defined(Q_OS_SYMBIAN) && defined(Q_CC_WINSCW) + // Symbian emulator startup simultaneously with this thread causes additional delay + QVERIFY(thread[x].cond.wait(&thread[x].readWriteLock, 10000)); +#else + QVERIFY(thread[x].cond.wait(&thread[x].readWriteLock, 1000)); +#endif + thread[x].readWriteLock.unlock(); + } + + for (x = 0; x < ThreadCount; ++x) { + QVERIFY(thread[x].isRunning()); + QVERIFY(!thread[x].isFinished()); + } + + for (x = 0; x < ThreadCount; ++x) { + thread[x].readWriteLock.lockForRead(); + thread[x].cond.wakeOne(); + thread[x].readWriteLock.unlock(); + } + + for (x = 0; x < ThreadCount; ++x) { + QVERIFY(thread[x].wait(1000)); + } + } + + { + // test multiple threads waiting on a wait condition + QReadWriteLock readWriteLock; + QWaitCondition cond1, cond2; + wait_QReadWriteLock_Thread_2 thread[ThreadCount]; + + readWriteLock.lockForWrite(); + for (x = 0; x < ThreadCount; ++x) { + thread[x].readWriteLock = &readWriteLock; + thread[x].cond = (x < ThreadCount / 2) ? &cond1 : &cond2; + thread[x].start(); + // wait for thread to start + QVERIFY(thread[x].started.wait(&readWriteLock, 1000)); + } + readWriteLock.unlock(); + + for (x = 0; x < ThreadCount; ++x) { + QVERIFY(thread[x].isRunning()); + QVERIFY(!thread[x].isFinished()); + } + + readWriteLock.lockForWrite(); + cond1.wakeAll(); + cond2.wakeAll(); + readWriteLock.unlock(); + + for (x = 0; x < ThreadCount; ++x) { + QVERIFY(thread[x].wait(1000)); + } + } + } + +} + +class wake_Thread : public QThread +{ +public: + static int count; + + QWaitCondition started; + QWaitCondition dummy; + + QMutex *mutex; + QWaitCondition *cond; + + inline wake_Thread() + : mutex(0), cond(0) + { } + + static inline void sleep(ulong s) + { QThread::sleep(s); } + + void run() + { + mutex->lock(); + ++count; + dummy.wakeOne(); // this wakeup should be lost + started.wakeOne(); + dummy.wakeAll(); // this one too + cond->wait(mutex); + --count; + mutex->unlock(); + } +}; + +int wake_Thread::count = 0; + +class wake_Thread_2 : public QThread +{ +public: + static int count; + + QWaitCondition started; + QWaitCondition dummy; + + QReadWriteLock *readWriteLock; + QWaitCondition *cond; + + inline wake_Thread_2() + : readWriteLock(0), cond(0) + { } + + static inline void sleep(ulong s) + { QThread::sleep(s); } + + void run() + { + readWriteLock->lockForWrite(); + ++count; + dummy.wakeOne(); // this wakeup should be lost + started.wakeOne(); + dummy.wakeAll(); // this one too + cond->wait(readWriteLock); + --count; + readWriteLock->unlock(); + } +}; + +int wake_Thread_2::count = 0; + +void tst_QWaitCondition::wakeOne() +{ + int x; + // wake up threads, one at a time + for (int i = 0; i < iterations; ++i) { + QMutex mutex; + QWaitCondition cond; + + // QMutex + wake_Thread thread[ThreadCount]; + bool thread_exited[ThreadCount]; + + mutex.lock(); + for (x = 0; x < ThreadCount; ++x) { + thread[x].mutex = &mutex; + thread[x].cond = &cond; + thread_exited[x] = FALSE; + thread[x].start(); + // wait for thread to start + QVERIFY(thread[x].started.wait(&mutex, 1000)); + // make sure wakeups are not queued... if nothing is + // waiting at the time of the wakeup, nothing happens + QVERIFY(!thread[x].dummy.wait(&mutex, 1)); + } + mutex.unlock(); + + QCOMPARE(wake_Thread::count, ThreadCount); + + // wake up threads one at a time + for (x = 0; x < ThreadCount; ++x) { + mutex.lock(); + cond.wakeOne(); + QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME)); + QVERIFY(!thread[x].dummy.wait(&mutex, 1)); + mutex.unlock(); + + int exited = 0; + for (int y = 0; y < ThreadCount; ++y) { + if (thread_exited[y]) + continue; + if (thread[y].wait(exited > 0 ? 10 : 1000)) { + thread_exited[y] = TRUE; + ++exited; + } + } + + QCOMPARE(exited, 1); + QCOMPARE(wake_Thread::count, ThreadCount - (x + 1)); + } + + QCOMPARE(wake_Thread::count, 0); + + // QReadWriteLock + QReadWriteLock readWriteLock; + wake_Thread_2 rwthread[ThreadCount]; + + readWriteLock.lockForWrite(); + for (x = 0; x < ThreadCount; ++x) { + rwthread[x].readWriteLock = &readWriteLock; + rwthread[x].cond = &cond; + thread_exited[x] = FALSE; + rwthread[x].start(); + // wait for thread to start + QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000)); + // make sure wakeups are not queued... if nothing is + // waiting at the time of the wakeup, nothing happens + QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1)); + } + readWriteLock.unlock(); + + QCOMPARE(wake_Thread_2::count, ThreadCount); + + // wake up threads one at a time + for (x = 0; x < ThreadCount; ++x) { + readWriteLock.lockForWrite(); + cond.wakeOne(); + QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME)); + QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1)); + readWriteLock.unlock(); + + int exited = 0; + for (int y = 0; y < ThreadCount; ++y) { + if (thread_exited[y]) + continue; + if (rwthread[y].wait(exited > 0 ? 10 : 1000)) { + thread_exited[y] = TRUE; + ++exited; + } + } + + QCOMPARE(exited, 1); + QCOMPARE(wake_Thread_2::count, ThreadCount - (x + 1)); + } + + QCOMPARE(wake_Thread_2::count, 0); + } + + // wake up threads, two at a time + for (int i = 0; i < iterations; ++i) { + QMutex mutex; + QWaitCondition cond; + + // QMutex + wake_Thread thread[ThreadCount]; + bool thread_exited[ThreadCount]; + + mutex.lock(); + for (x = 0; x < ThreadCount; ++x) { + thread[x].mutex = &mutex; + thread[x].cond = &cond; + thread_exited[x] = FALSE; + thread[x].start(); + // wait for thread to start + QVERIFY(thread[x].started.wait(&mutex, 1000)); + // make sure wakeups are not queued... if nothing is + // waiting at the time of the wakeup, nothing happens + QVERIFY(!thread[x].dummy.wait(&mutex, 1)); + } + mutex.unlock(); + + QCOMPARE(wake_Thread::count, ThreadCount); + + // wake up threads one at a time + for (x = 0; x < ThreadCount; x += 2) { + mutex.lock(); + cond.wakeOne(); + cond.wakeOne(); + QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME)); + QVERIFY(!thread[x].dummy.wait(&mutex, 1)); + QVERIFY(!thread[x + 1].dummy.wait(&mutex, 1)); + mutex.unlock(); + + int exited = 0; + for (int y = 0; y < ThreadCount; ++y) { + if (thread_exited[y]) + continue; + if (thread[y].wait(exited > 0 ? 10 : 1000)) { + thread_exited[y] = TRUE; + ++exited; + } + } + + QCOMPARE(exited, 2); + QCOMPARE(wake_Thread::count, ThreadCount - (x + 2)); + } + + QCOMPARE(wake_Thread::count, 0); + + // QReadWriteLock + QReadWriteLock readWriteLock; + wake_Thread_2 rwthread[ThreadCount]; + + readWriteLock.lockForWrite(); + for (x = 0; x < ThreadCount; ++x) { + rwthread[x].readWriteLock = &readWriteLock; + rwthread[x].cond = &cond; + thread_exited[x] = FALSE; + rwthread[x].start(); + // wait for thread to start + QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000)); + // make sure wakeups are not queued... if nothing is + // waiting at the time of the wakeup, nothing happens + QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1)); + } + readWriteLock.unlock(); + + QCOMPARE(wake_Thread_2::count, ThreadCount); + + // wake up threads one at a time + for (x = 0; x < ThreadCount; x += 2) { + readWriteLock.lockForWrite(); + cond.wakeOne(); + cond.wakeOne(); + QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME)); + QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1)); + QVERIFY(!rwthread[x + 1].dummy.wait(&readWriteLock, 1)); + readWriteLock.unlock(); + + int exited = 0; + for (int y = 0; y < ThreadCount; ++y) { + if (thread_exited[y]) + continue; + if (rwthread[y].wait(exited > 0 ? 10 : 1000)) { + thread_exited[y] = TRUE; + ++exited; + } + } + + QCOMPARE(exited, 2); + QCOMPARE(wake_Thread_2::count, ThreadCount - (x + 2)); + } + + QCOMPARE(wake_Thread_2::count, 0); +} +} + +void tst_QWaitCondition::wakeAll() +{ + int x; + for (int i = 0; i < iterations; ++i) { + QMutex mutex; + QWaitCondition cond; + + // QMutex + wake_Thread thread[ThreadCount]; + + mutex.lock(); + for (x = 0; x < ThreadCount; ++x) { + thread[x].mutex = &mutex; + thread[x].cond = &cond; + thread[x].start(); + // wait for thread to start + QVERIFY(thread[x].started.wait(&mutex, 1000)); + } + mutex.unlock(); + + QCOMPARE(wake_Thread::count, ThreadCount); + + // wake up all threads at once + mutex.lock(); + cond.wakeAll(); + QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME)); + mutex.unlock(); + + int exited = 0; + for (x = 0; x < ThreadCount; ++x) { + if (thread[x].wait(1000)) + ++exited; + } + + QCOMPARE(exited, ThreadCount); + QCOMPARE(wake_Thread::count, 0); + + // QReadWriteLock + QReadWriteLock readWriteLock; + wake_Thread_2 rwthread[ThreadCount]; + + readWriteLock.lockForWrite(); + for (x = 0; x < ThreadCount; ++x) { + rwthread[x].readWriteLock = &readWriteLock; + rwthread[x].cond = &cond; + rwthread[x].start(); + // wait for thread to start + QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000)); + } + readWriteLock.unlock(); + + QCOMPARE(wake_Thread_2::count, ThreadCount); + + // wake up all threads at once + readWriteLock.lockForWrite(); + cond.wakeAll(); + QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME)); + readWriteLock.unlock(); + + exited = 0; + for (x = 0; x < ThreadCount; ++x) { + if (rwthread[x].wait(1000)) + ++exited; + } + + QCOMPARE(exited, ThreadCount); + QCOMPARE(wake_Thread_2::count, 0); + } +} + +class wait_RaceConditionThread : public QThread +{ +public: + wait_RaceConditionThread(QMutex *mutex, QWaitCondition *startup, QWaitCondition *waitCondition, + ulong timeout = ULONG_MAX) + : timeout(timeout), returnValue(false), ready(false), + mutex(mutex), startup(startup), waitCondition(waitCondition) {} + + unsigned long timeout; + bool returnValue; + + bool ready; + + QMutex *mutex; + QWaitCondition *startup; + QWaitCondition *waitCondition; + + void run() { + mutex->lock(); + + ready = true; + startup->wakeOne(); + + returnValue = waitCondition->wait(mutex, timeout); + + mutex->unlock(); + } +}; + +class wait_RaceConditionThread_2 : public QThread +{ +public: + wait_RaceConditionThread_2(QReadWriteLock *readWriteLock, + QWaitCondition *startup, + QWaitCondition *waitCondition, + ulong timeout = ULONG_MAX) + : timeout(timeout), returnValue(false), ready(false), + readWriteLock(readWriteLock), startup(startup), waitCondition(waitCondition) + { } + + unsigned long timeout; + bool returnValue; + + bool ready; + + QReadWriteLock *readWriteLock; + QWaitCondition *startup; + QWaitCondition *waitCondition; + + void run() { + readWriteLock->lockForWrite(); + + ready = true; + startup->wakeOne(); + + returnValue = waitCondition->wait(readWriteLock, timeout); + + readWriteLock->unlock(); + } +}; + +void tst_QWaitCondition::wait_RaceCondition() +{ + { + QMutex mutex; + QWaitCondition startup; + QWaitCondition waitCondition; + + wait_RaceConditionThread timeoutThread(&mutex, &startup, &waitCondition, 1000), + waitingThread1(&mutex, &startup, &waitCondition); + + timeoutThread.start(); + waitingThread1.start(); + mutex.lock(); + + // wait for the threads to start up + while (!timeoutThread.ready + || !waitingThread1.ready) { + startup.wait(&mutex); + } + + QTest::qWait(2000); + + waitCondition.wakeOne(); + + mutex.unlock(); + + QVERIFY(timeoutThread.wait(5000)); + QVERIFY(!timeoutThread.returnValue); + QVERIFY(waitingThread1.wait(5000)); + QVERIFY(waitingThread1.returnValue); + } + + { + QReadWriteLock readWriteLock; + QWaitCondition startup; + QWaitCondition waitCondition; + + wait_RaceConditionThread_2 timeoutThread(&readWriteLock, &startup, &waitCondition, 1000), + waitingThread1(&readWriteLock, &startup, &waitCondition); + + timeoutThread.start(); + waitingThread1.start(); + readWriteLock.lockForRead(); + + // wait for the threads to start up + while (!timeoutThread.ready + || !waitingThread1.ready) { + startup.wait(&readWriteLock); + } + + QTest::qWait(2000); + + waitCondition.wakeOne(); + + readWriteLock.unlock(); + + QVERIFY(timeoutThread.wait(5000)); + QVERIFY(!timeoutThread.returnValue); + QVERIFY(waitingThread1.wait(5000)); + QVERIFY(waitingThread1.returnValue); + } +} + +QTEST_MAIN(tst_QWaitCondition) +#include "tst_qwaitcondition.moc" diff --git a/tests/auto/corelib/thread/qwritelocker/.gitignore b/tests/auto/corelib/thread/qwritelocker/.gitignore new file mode 100644 index 0000000000..865739ad2c --- /dev/null +++ b/tests/auto/corelib/thread/qwritelocker/.gitignore @@ -0,0 +1 @@ +tst_qwritelocker diff --git a/tests/auto/corelib/thread/qwritelocker/qwritelocker.pro b/tests/auto/corelib/thread/qwritelocker/qwritelocker.pro new file mode 100644 index 0000000000..39a98aaba5 --- /dev/null +++ b/tests/auto/corelib/thread/qwritelocker/qwritelocker.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +SOURCES += tst_qwritelocker.cpp +QT = core +CONFIG += parallel_test diff --git a/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp b/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp new file mode 100644 index 0000000000..a06431a5e1 --- /dev/null +++ b/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** 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 + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QWriteLockerThread : public QThread +{ +public: + QReadWriteLock lock; + QSemaphore semaphore, testSemaphore; + + void waitForTest() + { + semaphore.release(); + testSemaphore.acquire(); + } +}; + +class tst_QWriteLocker : public QObject +{ + Q_OBJECT + +public: + tst_QWriteLocker(); + ~tst_QWriteLocker(); + + tst_QWriteLockerThread *thread; + + void waitForThread() + { + thread->semaphore.acquire(); + } + void releaseThread() + { + thread->testSemaphore.release(); + } + +private slots: + void scopeTest(); + void unlockAndRelockTest(); + void lockerStateTest(); +}; + +tst_QWriteLocker::tst_QWriteLocker() +{ +} + +tst_QWriteLocker::~tst_QWriteLocker() +{ +} + +void tst_QWriteLocker::scopeTest() +{ + class ScopeTestThread : public tst_QWriteLockerThread + { + public: + void run() + { + waitForTest(); + + { + QWriteLocker locker(&lock); + waitForTest(); + } + + waitForTest(); + } + }; + + thread = new ScopeTestThread; + thread->start(); + + waitForThread(); + // lock should be unlocked before entering the scope that creates the QWriteLocker + QVERIFY(thread->lock.tryLockForWrite()); + thread->lock.unlock(); + releaseThread(); + + waitForThread(); + // lock should be locked by the QWriteLocker + QVERIFY(!thread->lock.tryLockForWrite()); + releaseThread(); + + waitForThread(); + // lock should be unlocked when the QWriteLocker goes out of scope + QVERIFY(thread->lock.tryLockForWrite()); + thread->lock.unlock(); + releaseThread(); + + QVERIFY(thread->wait()); + + delete thread; + thread = 0; +} + + +void tst_QWriteLocker::unlockAndRelockTest() +{ + class UnlockAndRelockThread : public tst_QWriteLockerThread + { + public: + void run() + { + QWriteLocker locker(&lock); + + waitForTest(); + + locker.unlock(); + + waitForTest(); + + locker.relock(); + + waitForTest(); + } + }; + + thread = new UnlockAndRelockThread; + thread->start(); + + waitForThread(); + // lock should be locked by the QWriteLocker + QVERIFY(!thread->lock.tryLockForWrite()); + releaseThread(); + + waitForThread(); + // lock has been explicitly unlocked via QWriteLocker + QVERIFY(thread->lock.tryLockForWrite()); + thread->lock.unlock(); + releaseThread(); + + waitForThread(); + // lock has been explicitly relocked via QWriteLocker + QVERIFY(!thread->lock.tryLockForWrite()); + releaseThread(); + + QVERIFY(thread->wait()); + + delete thread; + thread = 0; +} + +void tst_QWriteLocker::lockerStateTest() +{ + class LockerStateThread : public tst_QWriteLockerThread + { + public: + void run() + { + { + QWriteLocker locker(&lock); + locker.relock(); + locker.unlock(); + + waitForTest(); + } + + waitForTest(); + } + }; + + thread = new LockerStateThread; + thread->start(); + + waitForThread(); + // even though we relock() after creating the QWriteLocker, it shouldn't lock the lock more than once + QVERIFY(thread->lock.tryLockForWrite()); + thread->lock.unlock(); + releaseThread(); + + waitForThread(); + // if we call QWriteLocker::unlock(), its destructor should do nothing + QVERIFY(thread->lock.tryLockForWrite()); + thread->lock.unlock(); + releaseThread(); + + QVERIFY(thread->wait()); + + delete thread; + thread = 0; +} + +QTEST_MAIN(tst_QWriteLocker) +#include "tst_qwritelocker.moc" diff --git a/tests/auto/corelib/thread/thread.pro b/tests/auto/corelib/thread/thread.pro new file mode 100644 index 0000000000..a137748b75 --- /dev/null +++ b/tests/auto/corelib/thread/thread.pro @@ -0,0 +1,14 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qatomicint \ + qatomicpointer \ + qmutex \ + qmutexlocker \ + qreadlocker \ + qreadwritelock \ + qsemaphore \ + qthread \ + qthreadonce \ + qthreadstorage \ + qwaitcondition \ + qwritelocker -- cgit v1.2.3