summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib')
-rw-r--r--tests/auto/corelib/corelib.pro3
-rw-r--r--tests/auto/corelib/thread/qatomicint/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qatomicint/qatomicint.pro4
-rw-r--r--tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp791
-rw-r--r--tests/auto/corelib/thread/qatomicpointer/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qatomicpointer/qatomicpointer.pro4
-rw-r--r--tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp674
-rw-r--r--tests/auto/corelib/thread/qmutex/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qmutex/qmutex.pro5
-rw-r--r--tests/auto/corelib/thread/qmutex/tst_qmutex.cpp640
-rw-r--r--tests/auto/corelib/thread/qmutexlocker/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qmutexlocker/qmutexlocker.pro4
-rw-r--r--tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp236
-rw-r--r--tests/auto/corelib/thread/qreadlocker/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qreadlocker/qreadlocker.pro4
-rw-r--r--tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp231
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro4
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp1140
-rw-r--r--tests/auto/corelib/thread/qsemaphore/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qsemaphore/qsemaphore.pro7
-rw-r--r--tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp450
-rw-r--r--tests/auto/corelib/thread/qthread/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qthread/qthread.pro5
-rw-r--r--tests/auto/corelib/thread/qthread/tst_qthread.cpp1249
-rw-r--r--tests/auto/corelib/thread/qthreadonce/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp121
-rw-r--r--tests/auto/corelib/thread/qthreadonce/qthreadonce.h114
-rw-r--r--tests/auto/corelib/thread/qthreadonce/qthreadonce.pro13
-rw-r--r--tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp234
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/crashOnExit.cpp64
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/crashOnExit.pro4
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro5
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp503
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.pro4
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro6
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp845
-rw-r--r--tests/auto/corelib/thread/qwritelocker/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qwritelocker/qwritelocker.pro4
-rw-r--r--tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp231
-rw-r--r--tests/auto/corelib/thread/thread.pro14
43 files changed, 7624 insertions, 1 deletions
diff --git a/tests/auto/corelib/corelib.pro b/tests/auto/corelib/corelib.pro
index 03a7035af0..62532b968f 100644
--- a/tests/auto/corelib/corelib.pro
+++ b/tests/auto/corelib/corelib.pro
@@ -7,4 +7,5 @@ SUBDIRS=\
io \
kernel \
plugin \
- statemachine
+ statemachine \
+ 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 <QtTest/QtTest>
+
+#include <QAtomicInt>
+#include <QCoreApplication>
+
+#include <limits.h>
+
+//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<int>("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<int>("value1");
+ QTest::addColumn<int>("value2");
+ QTest::addColumn<int>("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<int>("value1");
+ QTest::addColumn<int>("value2");
+ QTest::addColumn<int>("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<int>("value");
+ QTest::addColumn<int>("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<int>("value");
+ QTest::addColumn<int>("result");
+ QTest::addColumn<int>("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<int>("value");
+ QTest::addColumn<int>("result");
+ QTest::addColumn<int>("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<int>("value");
+ QTest::addColumn<int>("expected");
+ QTest::addColumn<int>("newval");
+ QTest::addColumn<int>("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<int>("value");
+ QTest::addColumn<int>("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<int>("value1");
+ QTest::addColumn<int>("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 <QtTest/QtTest>
+
+#include <qatomic.h>
+
+#include <limits.h>
+
+//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<WFHC> 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<void> atomic1 = one;
+ QVERIFY(atomic1 == one);
+
+ void *two = &one;
+ QAtomicPointer<void> atomic2 = two;
+ QVERIFY(atomic2 == two);
+
+ void *three = &two;
+ QAtomicPointer<void> atomic3 = three;
+ QVERIFY(atomic3 == three);
+}
+
+void tst_QAtomicPointer::copy_constructor()
+{
+ void *one = this;
+ QAtomicPointer<void> atomic1 = one;
+ QAtomicPointer<void> atomic1_copy = atomic1;
+ QVERIFY(atomic1_copy == one);
+ QCOMPARE(atomic1_copy, atomic1);
+
+ void *two = &one;
+ QAtomicPointer<void> atomic2 = two;
+ QAtomicPointer<void> atomic2_copy = atomic2;
+ QVERIFY(atomic2_copy == two);
+ QCOMPARE(atomic2_copy, atomic2);
+
+ void *three = &two;
+ QAtomicPointer<void> atomic3 = three;
+ QAtomicPointer<void> 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<void> atomic1 = one;
+ QAtomicPointer<void> atomic2 = two;
+ QAtomicPointer<void> 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<void> atomic1 = one;
+ QAtomicPointer<void> atomic2 = two;
+ QAtomicPointer<void> 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<void> atomic1 = one;
+ QAtomicPointer<void> atomic2 = two;
+ QAtomicPointer<void> 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<Type> p = &t;
+ QCOMPARE((*p).self(), t.self());
+}
+
+void tst_QAtomicPointer::dereference_operator()
+{
+ Type t;
+ QAtomicPointer<Type> 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<void>::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<void>::isTestAndSetNative()
+ || !QAtomicPointer<void>::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<void>::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<void>::isTestAndSetWaitFree());
+
+ // enforce some invariants
+ QVERIFY(QAtomicPointer<void>::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<void>::isTestAndSetWaitFree());
+#endif
+}
+
+void tst_QAtomicPointer::testAndSet()
+{
+ void *one = this;
+ void *two = &one;
+ void *three = &two;
+
+ {
+ QAtomicPointer<void> atomic1 = one;
+ QAtomicPointer<void> atomic2 = two;
+ QAtomicPointer<void> 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<void> atomic1 = one;
+ QAtomicPointer<void> atomic2 = two;
+ QAtomicPointer<void> 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<void> atomic1 = one;
+ QAtomicPointer<void> atomic2 = two;
+ QAtomicPointer<void> 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<void> atomic1 = one;
+ QAtomicPointer<void> atomic2 = two;
+ QAtomicPointer<void> 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<void>::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<void>::isFetchAndStoreNative()
+ || !QAtomicPointer<void>::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<void>::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<void>::isFetchAndStoreWaitFree());
+
+ // enforce some invariants
+ QVERIFY(QAtomicPointer<void>::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<void>::isFetchAndStoreWaitFree());
+#endif
+}
+
+void tst_QAtomicPointer::fetchAndStore()
+{
+ void *one = this;
+ void *two = &one;
+ void *three = &two;
+
+ {
+ QAtomicPointer<void> atomic1 = one;
+ QAtomicPointer<void> atomic2 = two;
+ QAtomicPointer<void> 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<void> atomic1 = one;
+ QAtomicPointer<void> atomic2 = two;
+ QAtomicPointer<void> 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<void> atomic1 = one;
+ QAtomicPointer<void> atomic2 = two;
+ QAtomicPointer<void> 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<void> atomic1 = one;
+ QAtomicPointer<void> atomic2 = two;
+ QAtomicPointer<void> 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<void>::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<void>::isFetchAndAddNative()
+ || !QAtomicPointer<void>::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<void>::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<void>::isFetchAndAddWaitFree());
+
+ // enforce some invariants
+ QVERIFY(QAtomicPointer<void>::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<void>::isFetchAndAddWaitFree());
+#endif
+}
+
+void tst_QAtomicPointer::fetchAndAdd_data()
+{
+ QTest::addColumn<int>("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<char> pointer1 = pc;
+ QCOMPARE(quintptr(pointer1.fetchAndAddRelaxed(valueToAdd)), quintptr(pc));
+ QCOMPARE(quintptr(pointer1.fetchAndAddRelaxed(-valueToAdd)), quintptr(pc + valueToAdd));
+ QCOMPARE(quintptr(static_cast<char *>(pointer1)), quintptr(pc));
+ QAtomicPointer<short> pointer2 = ps;
+ QCOMPARE(quintptr(pointer2.fetchAndAddRelaxed(valueToAdd)), quintptr(ps));
+ QCOMPARE(quintptr(pointer2.fetchAndAddRelaxed(-valueToAdd)), quintptr(ps + valueToAdd));
+ QCOMPARE(quintptr(static_cast<short *>(pointer2)), quintptr(ps));
+ QAtomicPointer<int> pointer3 = pi;
+ QCOMPARE(quintptr(pointer3.fetchAndAddRelaxed(valueToAdd)), quintptr(pi));
+ QCOMPARE(quintptr(pointer3.fetchAndAddRelaxed(-valueToAdd)), quintptr(pi + valueToAdd));
+ QCOMPARE(quintptr(static_cast<int *>(pointer3)), quintptr(pi));
+ }
+
+ {
+ QAtomicPointer<char> pointer1 = pc;
+ QCOMPARE(quintptr(pointer1.fetchAndAddAcquire(valueToAdd)), quintptr(pc));
+ QCOMPARE(quintptr(pointer1.fetchAndAddAcquire(-valueToAdd)), quintptr(pc + valueToAdd));
+ QCOMPARE(quintptr(static_cast<char *>(pointer1)), quintptr(pc));
+ QAtomicPointer<short> pointer2 = ps;
+ QCOMPARE(quintptr(pointer2.fetchAndAddAcquire(valueToAdd)), quintptr(ps));
+ QCOMPARE(quintptr(pointer2.fetchAndAddAcquire(-valueToAdd)), quintptr(ps + valueToAdd));
+ QCOMPARE(quintptr(static_cast<short *>(pointer2)), quintptr(ps));
+ QAtomicPointer<int> pointer3 = pi;
+ QCOMPARE(quintptr(pointer3.fetchAndAddAcquire(valueToAdd)), quintptr(pi));
+ QCOMPARE(quintptr(pointer3.fetchAndAddAcquire(-valueToAdd)), quintptr(pi + valueToAdd));
+ QCOMPARE(quintptr(static_cast<int *>(pointer3)), quintptr(pi));
+ }
+
+ {
+ QAtomicPointer<char> pointer1 = pc;
+ QCOMPARE(quintptr(pointer1.fetchAndAddRelease(valueToAdd)), quintptr(pc));
+ QCOMPARE(quintptr(pointer1.fetchAndAddRelease(-valueToAdd)), quintptr(pc + valueToAdd));
+ QCOMPARE(quintptr(static_cast<char *>(pointer1)), quintptr(pc));
+ QAtomicPointer<short> pointer2 = ps;
+ QCOMPARE(quintptr(pointer2.fetchAndAddRelease(valueToAdd)), quintptr(ps));
+ QCOMPARE(quintptr(pointer2.fetchAndAddRelease(-valueToAdd)), quintptr(ps + valueToAdd));
+ QCOMPARE(quintptr(static_cast<short *>(pointer2)), quintptr(ps));
+ QAtomicPointer<int> pointer3 = pi;
+ QCOMPARE(quintptr(pointer3.fetchAndAddRelease(valueToAdd)), quintptr(pi));
+ QCOMPARE(quintptr(pointer3.fetchAndAddRelease(-valueToAdd)), quintptr(pi + valueToAdd));
+ QCOMPARE(quintptr(static_cast<int *>(pointer3)), quintptr(pi));
+ }
+
+ {
+ QAtomicPointer<char> pointer1 = pc;
+ QCOMPARE(quintptr(pointer1.fetchAndAddOrdered(valueToAdd)), quintptr(pc));
+ QCOMPARE(quintptr(pointer1.fetchAndAddOrdered(-valueToAdd)), quintptr(pc + valueToAdd));
+ QCOMPARE(quintptr(static_cast<char *>(pointer1)), quintptr(pc));
+ QAtomicPointer<short> pointer2 = ps;
+ QCOMPARE(quintptr(pointer2.fetchAndAddOrdered(valueToAdd)), quintptr(ps));
+ QCOMPARE(quintptr(pointer2.fetchAndAddOrdered(-valueToAdd)), quintptr(ps + valueToAdd));
+ QCOMPARE(quintptr(static_cast<short *>(pointer2)), quintptr(ps));
+ QAtomicPointer<int> pointer3 = pi;
+ QCOMPARE(quintptr(pointer3.fetchAndAddOrdered(valueToAdd)), quintptr(pi));
+ QCOMPARE(quintptr(pointer3.fetchAndAddOrdered(-valueToAdd)), quintptr(pi + valueToAdd));
+ QCOMPARE(quintptr(static_cast<int *>(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 <QtTest/QtTest>
+
+#include <qatomic.h>
+#include <qcoreapplication.h>
+#include <qdatetime.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qwaitcondition.h>
+
+//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 <QtTest/QtTest>
+
+#include <QCoreApplication>
+#include <QMutexLocker>
+#include <QSemaphore>
+#include <QThread>
+
+//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 <QtTest/QtTest>
+
+#include <QCoreApplication>
+#include <QReadLocker>
+#include <QSemaphore>
+#include <QThread>
+
+//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 <QtTest/QtTest>
+#include <qcoreapplication.h>
+
+
+#include <qreadwritelock.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qwaitcondition.h>
+
+#ifdef Q_OS_UNIX
+#include <unistd.h>
+#endif
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+#include <windows.h>
+#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 <stdio.h>
+
+//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<runs; ++i) {
+ rwlock.lockForRead();
+ rwlock.unlock();
+ }
+}
+
+void tst_QReadWriteLock::writeLockUnlockLoop()
+{
+ QReadWriteLock rwlock;
+ int runs=10000;
+ int i;
+ for (i=0; i<runs; ++i) {
+ rwlock.lockForWrite();
+ rwlock.unlock();
+ }
+}
+
+
+void tst_QReadWriteLock::readLockLoop()
+{
+ QReadWriteLock rwlock;
+ int runs=10000;
+ int i;
+ for (i=0; i<runs; ++i) {
+ rwlock.lockForRead();
+ }
+ for (i=0; i<runs; ++i) {
+ rwlock.unlock();
+ }
+}
+
+void tst_QReadWriteLock::writeLockLoop()
+{
+ /*
+ If you include this, the test should print one line
+ and then block.
+ */
+#if 0
+ QReadWriteLock rwlock;
+ int runs=10000;
+ int i;
+ for (i=0; i<runs; ++i) {
+ rwlock.lockForWrite();
+ qDebug("I am going to block now.");
+ }
+#endif
+}
+
+void tst_QReadWriteLock::readWriteLockUnlockLoop()
+{
+ QReadWriteLock rwlock;
+ int runs=10000;
+ int i;
+ for (i=0; i<runs; ++i) {
+ rwlock.lockForRead();
+ rwlock.unlock();
+ rwlock.lockForWrite();
+ rwlock.unlock();
+ }
+
+}
+
+QAtomicInt lockCount(0);
+QReadWriteLock readWriteLock;
+QSemaphore testsTurn;
+QSemaphore threadsTurn;
+
+
+void tst_QReadWriteLock::tryReadLock()
+{
+ QReadWriteLock rwlock;
+ QVERIFY(rwlock.tryLockForRead());
+ rwlock.unlock();
+ QVERIFY(rwlock.tryLockForRead());
+ rwlock.unlock();
+
+ rwlock.lockForRead();
+ rwlock.lockForRead();
+ QVERIFY(rwlock.tryLockForRead());
+ rwlock.unlock();
+ rwlock.unlock();
+ rwlock.unlock();
+
+ rwlock.lockForWrite();
+ QVERIFY(!rwlock.tryLockForRead());
+ rwlock.unlock();
+
+ // functionality test
+ {
+ class Thread : public QThread
+ {
+ public:
+ void run()
+ {
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ QVERIFY(!readWriteLock.tryLockForRead());
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ QVERIFY(readWriteLock.tryLockForRead());
+ lockCount.ref();
+ QVERIFY(readWriteLock.tryLockForRead());
+ lockCount.ref();
+ lockCount.deref();
+ readWriteLock.unlock();
+ lockCount.deref();
+ readWriteLock.unlock();
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ QTime timer;
+ timer.start();
+ QVERIFY(!readWriteLock.tryLockForRead(1000));
+ QVERIFY(timer.elapsed() >= 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()<runTime) {
+ testRwlock.lockForRead();
+ if(print) printf("reading\n");
+ if (holdTime) msleep(holdTime);
+ testRwlock.unlock();
+ if (waitTime) msleep(waitTime);
+ }
+ }
+};
+
+/*
+ for(runTime msecs)
+ write-lock
+ msleep(holdTime msecs)
+ release lock
+ msleep(waitTime msecs)
+*/
+class WriteLockLoopThread : public QThread
+{
+public:
+ QReadWriteLock &testRwlock;
+ int runTime;
+ int holdTime;
+ int waitTime;
+ bool print;
+ QTime t;
+ inline WriteLockLoopThread(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() < runTime) {
+ testRwlock.lockForWrite();
+ if (print) printf(".");
+ if (holdTime) msleep(holdTime);
+ testRwlock.unlock();
+ if (waitTime) msleep(waitTime);
+ }
+ }
+};
+
+volatile int count=0;
+
+/*
+ for(runTime msecs)
+ write-lock
+ count to maxval
+ set count to 0
+ release lock
+ msleep waitTime
+*/
+class WriteLockCountThread : public QThread
+{
+public:
+ QReadWriteLock &testRwlock;
+ int runTime;
+ int waitTime;
+ int maxval;
+ QTime t;
+ inline WriteLockCountThread(QReadWriteLock &l, int runTime, int waitTime, int maxval)
+ :testRwlock(l)
+ ,runTime(runTime)
+ ,waitTime(waitTime)
+ ,maxval(maxval)
+ { }
+ void run()
+ {
+ t.start();
+ while (t.elapsed() < runTime) {
+ testRwlock.lockForWrite();
+ if(count)
+ qFatal("Non-zero count at start of write! (%d)",count );
+// printf(".");
+ int i;
+ for(i=0; i<maxval; ++i) {
+ volatile int lc=count;
+ ++lc;
+ count=lc;
+ }
+ count=0;
+ testRwlock.unlock();
+ msleep(waitTime);
+ }
+ }
+};
+
+/*
+ for(runTime msecs)
+ read-lock
+ verify count==0
+ release lock
+ msleep waitTime
+*/
+class ReadLockCountThread : public QThread
+{
+public:
+ QReadWriteLock &testRwlock;
+ int runTime;
+ int waitTime;
+ QTime t;
+ inline ReadLockCountThread(QReadWriteLock &l, int runTime, int waitTime)
+ :testRwlock(l)
+ ,runTime(runTime)
+ ,waitTime(waitTime)
+ { }
+ void run()
+ {
+ t.start();
+ while (t.elapsed() < runTime) {
+ testRwlock.lockForRead();
+ if(count)
+ qFatal("Non-zero count at Read! (%d)",count );
+ testRwlock.unlock();
+ msleep(waitTime);
+ }
+ }
+};
+
+
+/*
+ A writer acquires a read-lock, a reader locks
+ the writer releases the lock, the reader gets the lock
+*/
+void tst_QReadWriteLock::readLockBlockRelease()
+{
+ QReadWriteLock testLock;
+ testLock.lockForWrite();
+ threadDone=false;
+ ReadLockThread rlt(testLock);
+ rlt.start();
+ sleep(1);
+ testLock.unlock();
+ rlt.wait();
+ QVERIFY(threadDone);
+}
+
+/*
+ writer1 acquires a read-lock, writer2 blocks,
+ writer1 releases the lock, writer2 gets the lock
+*/
+void tst_QReadWriteLock::writeLockBlockRelease()
+{
+ QReadWriteLock testLock;
+ testLock.lockForWrite();
+ threadDone=false;
+ WriteLockThread wlt(testLock);
+ wlt.start();
+ sleep(1);
+ testLock.unlock();
+ wlt.wait();
+ QVERIFY(threadDone);
+}
+/*
+ Two readers acquire a read-lock, one writer attempts a write block,
+ the readers release their locks, the writer gets the lock.
+*/
+void tst_QReadWriteLock::multipleReadersBlockRelease()
+{
+
+ QReadWriteLock testLock;
+ release=false;
+ threadDone=false;
+ ReadLockReleasableThread rlt1(testLock);
+ ReadLockReleasableThread rlt2(testLock);
+ rlt1.start();
+ rlt2.start();
+ sleep(1);
+ WriteLockThread wlt(testLock);
+ wlt.start();
+ sleep(1);
+ release=true;
+ wlt.wait();
+ rlt1.wait();
+ rlt2.wait();
+ QVERIFY(threadDone);
+}
+
+/*
+ Multiple readers locks and unlocks a lock.
+*/
+void tst_QReadWriteLock::multipleReadersLoop()
+{
+ int time=500;
+ int hold=250;
+ int wait=0;
+#if defined (Q_OS_HPUX)
+ const int numthreads=50;
+#elif defined(Q_OS_VXWORKS)
+ const int numthreads=40;
+#else
+ const int numthreads=75;
+#endif
+ QReadWriteLock testLock;
+ ReadLockLoopThread *threads[numthreads];
+ int i;
+ for (i=0; i<numthreads; ++i)
+ threads[i] = new ReadLockLoopThread(testLock, time, hold, wait);
+ for (i=0; i<numthreads; ++i)
+ threads[i]->start();
+ for (i=0; i<numthreads; ++i)
+ threads[i]->wait();
+ for (i=0; i<numthreads; ++i)
+ delete threads[i];
+}
+
+/*
+ Multiple writers locks and unlocks a lock.
+*/
+void tst_QReadWriteLock::multipleWritersLoop()
+{
+ int time=500;
+ int wait=0;
+ int hold=0;
+ const int numthreads=50;
+ QReadWriteLock testLock;
+ WriteLockLoopThread *threads[numthreads];
+ int i;
+ for (i=0; i<numthreads; ++i)
+ threads[i] = new WriteLockLoopThread(testLock, time, hold, wait);
+ for (i=0; i<numthreads; ++i)
+ threads[i]->start();
+ for (i=0; i<numthreads; ++i)
+ threads[i]->wait();
+ for (i=0; i<numthreads; ++i)
+ delete threads[i];
+}
+
+/*
+ Multiple readers and writers locks and unlocks a lock.
+*/
+void tst_QReadWriteLock::multipleReadersWritersLoop()
+{
+ //int time=INT_MAX;
+ int time=10000;
+ int readerThreads=20;
+ int readerWait=0;
+ int readerHold=1;
+
+ int writerThreads=2;
+ int writerWait=500;
+ int writerHold=50;
+
+ QReadWriteLock testLock;
+ ReadLockLoopThread *readers[1024];
+ WriteLockLoopThread *writers[1024];
+ int i;
+
+ for (i=0; i<readerThreads; ++i)
+ readers[i] = new ReadLockLoopThread(testLock, time, readerHold, readerWait, false);
+ for (i=0; i<writerThreads; ++i)
+ writers[i] = new WriteLockLoopThread(testLock, time, writerHold, writerWait, false);
+
+ for (i=0; i<readerThreads; ++i)
+ readers[i]->start(QThread::NormalPriority);
+ for (i=0; i<writerThreads; ++i)
+ writers[i]->start(QThread::IdlePriority);
+
+ for (i=0; i<readerThreads; ++i)
+ readers[i]->wait();
+ for (i=0; i<writerThreads; ++i)
+ writers[i]->wait();
+
+ for (i=0; i<readerThreads; ++i)
+ delete readers[i];
+ for (i=0; i<writerThreads; ++i)
+ delete writers[i];
+}
+
+/*
+ Writers increment a variable from 0 to maxval, then reset it to 0.
+ Readers verify that the variable remains at 0.
+*/
+void tst_QReadWriteLock::countingTest()
+{
+ //int time=INT_MAX;
+ int time=10000;
+ int readerThreads=20;
+ int readerWait=1;
+
+ int writerThreads=3;
+ int writerWait=150;
+ int maxval=10000;
+
+ QReadWriteLock testLock;
+ ReadLockCountThread *readers[1024];
+ WriteLockCountThread *writers[1024];
+ int i;
+
+ for (i=0; i<readerThreads; ++i)
+ readers[i] = new ReadLockCountThread(testLock, time, readerWait);
+ for (i=0; i<writerThreads; ++i)
+ writers[i] = new WriteLockCountThread(testLock, time, writerWait, maxval);
+
+ for (i=0; i<readerThreads; ++i)
+ readers[i]->start(QThread::NormalPriority);
+ for (i=0; i<writerThreads; ++i)
+ writers[i]->start(QThread::LowestPriority);
+
+ for (i=0; i<readerThreads; ++i)
+ readers[i]->wait();
+ for (i=0; i<writerThreads; ++i)
+ writers[i]->wait();
+
+ for (i=0; i<readerThreads; ++i)
+ delete readers[i];
+ for (i=0; i<writerThreads; ++i)
+ delete writers[i];
+}
+
+void tst_QReadWriteLock::limitedReaders()
+{
+
+};
+
+/*
+ Test a race-condition that may happen if one thread is in unlock() while
+ another thread deletes the rw-lock.
+
+ MainThread DeleteOnUnlockThread
+
+ write-lock
+ unlock
+ | write-lock
+ | unlock
+ | delete lock
+ deref d inside unlock
+*/
+class DeleteOnUnlockThread : public QThread
+{
+public:
+ DeleteOnUnlockThread(QReadWriteLock **lock, QWaitCondition *startup, QMutex *waitMutex)
+ :m_lock(lock), m_startup(startup), m_waitMutex(waitMutex) {}
+ void run()
+ {
+ m_waitMutex->lock();
+ 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() <millisecs)
+ {
+ ++count;
+ }
+ }
+ {
+ QReadWriteLock rwlock;
+ QTime t;
+ t.start();
+ while(t.elapsed() <millisecs)
+ {
+ rwlock.lockForRead();
+ rwlock.unlock();
+ ++read;
+ }
+ }
+ {
+ QReadWriteLock rwlock;
+ QTime t;
+ t.start();
+ while(t.elapsed() <millisecs)
+ {
+ rwlock.lockForWrite();
+ rwlock.unlock();
+ ++write;
+ }
+ }
+
+ qDebug("during %d millisecs:", millisecs);
+ qDebug("counted to %u", count);
+ qDebug("%u uncontended read locks/unlocks", read);
+ qDebug("%u uncontended write locks/unlocks", write);
+}
+
+enum { RecursiveLockCount = 10 };
+
+void tst_QReadWriteLock::recursiveReadLock()
+{
+ // thread to attempt locking for writing while the test recursively locks for reading
+ class RecursiveReadLockThread : public QThread
+ {
+ public:
+ QReadWriteLock *lock;
+ bool tryLockForWriteResult;
+
+ void run()
+ {
+ testsTurn.release();
+
+ // test is recursively locking for writing
+ for (int i = 0; i < RecursiveLockCount; ++i) {
+ threadsTurn.acquire();
+ tryLockForWriteResult = lock->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 <QtTest/QtTest>
+
+
+#include <qcoreapplication.h>
+#include <qthread.h>
+#include <qsemaphore.h>
+
+//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<int>("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) qDebug() << #a << "=" << e << " !>= " << #b << "=" << b; QVERIFY(e>=b);}
+#define QVERIFYLE(a,b) {int e = a; if (b<a) qDebug() << #a << "=" << e << " !<= " << #b << "=" << b; QVERIFY(e<=b);}
+
+ QCOMPARE(semaphore.available(), 0);
+
+ semaphore.release();
+ QCOMPARE(semaphore.available(), 1);
+ time.start();
+ QVERIFY(!semaphore.tryAcquire(2, timeout));
+ QVERIFYGE(time.elapsed(), timeout);
+ QCOMPARE(semaphore.available(), 1);
+
+ semaphore.release();
+ QCOMPARE(semaphore.available(), 2);
+ time.start();
+ QVERIFY(!semaphore.tryAcquire(3, timeout));
+ QVERIFYGE(time.elapsed(), timeout);
+ QCOMPARE(semaphore.available(), 2);
+
+ semaphore.release(10);
+ QCOMPARE(semaphore.available(), 12);
+ time.start();
+ QVERIFY(!semaphore.tryAcquire(100, timeout));
+ QVERIFYGE(time.elapsed(), timeout);
+ QCOMPARE(semaphore.available(), 12);
+
+ semaphore.release(10);
+ QCOMPARE(semaphore.available(), 22);
+ time.start();
+ QVERIFY(!semaphore.tryAcquire(100, timeout));
+ QVERIFYGE(time.elapsed(), timeout);
+ QCOMPARE(semaphore.available(), 22);
+
+ time.start();
+ QVERIFY(semaphore.tryAcquire(1, timeout));
+ QVERIFYLE(time.elapsed(), timeout);
+ QCOMPARE(semaphore.available(), 21);
+
+ time.start();
+ QVERIFY(semaphore.tryAcquire(1, timeout));
+ QVERIFYLE(time.elapsed(), timeout);
+ QCOMPARE(semaphore.available(), 20);
+
+ time.start();
+ QVERIFY(semaphore.tryAcquire(10, timeout));
+ QVERIFYLE(time.elapsed(), timeout);
+ QCOMPARE(semaphore.available(), 10);
+
+ time.start();
+ QVERIFY(semaphore.tryAcquire(10, timeout));
+ QVERIFYLE(time.elapsed(), timeout);
+ QCOMPARE(semaphore.available(), 0);
+
+ // should not be able to acquire more
+ time.start();
+ QVERIFY(!semaphore.tryAcquire(1, timeout));
+ QVERIFYGE(time.elapsed(), timeout);
+ QCOMPARE(semaphore.available(), 0);
+
+ time.start();
+ QVERIFY(!semaphore.tryAcquire(1, timeout));
+ QVERIFYGE(time.elapsed(), timeout);
+ QCOMPARE(semaphore.available(), 0);
+
+ time.start();
+ QVERIFY(!semaphore.tryAcquire(10, timeout));
+ QVERIFYGE(time.elapsed(), timeout);
+ QCOMPARE(semaphore.available(), 0);
+
+ time.start();
+ QVERIFY(!semaphore.tryAcquire(10, timeout));
+ QVERIFYGE(time.elapsed(), timeout);
+ QCOMPARE(semaphore.available(), 0);
+}
+
+void tst_QSemaphore::tryAcquireWithTimeoutStarvation()
+{
+ class Thread : public QThread
+ {
+ public:
+ QSemaphore startup;
+ QSemaphore *semaphore;
+ int amountToConsume, timeout;
+
+ void run()
+ {
+ startup.release();
+ forever {
+ if (!semaphore->tryAcquire(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 <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qdatetime.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qtimer.h>
+#include <qwaitcondition.h>
+#include <qdebug.h>
+#include <qmetaobject.h>
+
+#ifdef Q_OS_UNIX
+#include <pthread.h>
+#endif
+#if defined(Q_OS_WINCE)
+#include <windows.h>
+#elif defined(Q_OS_WIN)
+#include <process.h>
+#include <windows.h>
+#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<NativeThreadWrapper*>(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<QThread **>(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<NativeThreadWrapper*> 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<NativeThreadWrapper*> 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<QObject> 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<QThread> weak(static_cast<QThread*>(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 <QtCore/qglobal.h>
+#include <QtCore/qatomic.h>
+
+QT_BEGIN_HEADER
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_THREAD
+
+class QOnceControl
+{
+public:
+ QOnceControl(QBasicAtomicInt *);
+ ~QOnceControl();
+
+ bool mustRunCode();
+ void done();
+
+private:
+ QBasicAtomicInt *gv;
+ union {
+ qint32 extra;
+ void *d;
+ };
+};
+
+#define Q_ONCE_GV_NAME2(prefix, line) prefix ## line
+#define Q_ONCE_GV_NAME(prefix, line) Q_ONCE_GV_NAME2(prefix, line)
+#define Q_ONCE_GV Q_ONCE_GV_NAME(_q_once_, __LINE__)
+
+#define Q_ONCE \
+ static QBasicAtomicInt Q_ONCE_GV = Q_BASIC_ATOMIC_INITIALIZER(0); \
+ if (0){} else \
+ for (QOnceControl _control_(&Q_ONCE_GV); _control_.mustRunCode(); _control_.done())
+
+template<typename T>
+class QSingleton
+{
+ // this is a POD-like class
+ struct Destructor
+ {
+ T *&pointer;
+ Destructor(T *&ptr) : pointer(ptr) {}
+ ~Destructor() { delete pointer; }
+ };
+
+public:
+ T *_q_value;
+ QBasicAtomicInt _q_guard;
+
+ inline T *value()
+ {
+ for (QOnceControl control(&_q_guard); control.mustRunCode(); control.done()) {
+ _q_value = new T();
+ static Destructor cleanup(_q_value);
+ }
+ return _q_value;
+ }
+
+ inline T& operator*() { return *value(); }
+ inline T* operator->() { return value(); }
+ inline operator T*() { return value(); }
+};
+
+#endif // QT_NO_THREAD
+
+QT_END_HEADER
+
+#endif
diff --git a/tests/auto/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 <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qwaitcondition.h>
+#include "qthreadonce.h"
+
+//TESTED_CLASS=
+//TESTED_FILES=corelib/thread/qthreadonce.h corelib/thread/qthreadonce.cpp
+
+class tst_QThreadOnce : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void sameThread();
+ void sameThread_data();
+ void multipleThreads();
+
+ void nesting();
+ void reentering();
+ void exception();
+};
+
+class SingletonObject: public QObject
+{
+ Q_OBJECT
+public:
+ static int runCount;
+ SingletonObject() { val = 42; ++runCount; }
+ ~SingletonObject() { }
+
+ QBasicAtomicInt val;
+};
+
+class IncrementThread: public QThread
+{
+public:
+ static QBasicAtomicInt runCount;
+ static QSingleton<SingletonObject> singleton;
+ QSemaphore &sem1, &sem2;
+ int &var;
+
+ IncrementThread(QSemaphore *psem1, QSemaphore *psem2, int *pvar, QObject *parent)
+ : QThread(parent), sem1(*psem1), sem2(*psem2), var(*pvar)
+ { start(); }
+
+ ~IncrementThread() { wait(); }
+
+protected:
+ void run()
+ {
+ sem2.release();
+ sem1.acquire(); // synchronize
+
+ Q_ONCE {
+ ++var;
+ }
+ runCount.ref();
+ singleton->val.ref();
+ }
+};
+int SingletonObject::runCount = 0;
+QBasicAtomicInt IncrementThread::runCount = Q_BASIC_ATOMIC_INITIALIZER(0);
+QSingleton<SingletonObject> IncrementThread::singleton;
+
+void tst_QThreadOnce::sameThread_data()
+{
+ SingletonObject::runCount = 0;
+ QTest::addColumn<int>("expectedValue");
+
+ QTest::newRow("first") << 42;
+ QTest::newRow("second") << 43;
+}
+
+void tst_QThreadOnce::sameThread()
+{
+ static int controlVariable = 0;
+ Q_ONCE {
+ QCOMPARE(controlVariable, 0);
+ ++controlVariable;
+ }
+ QCOMPARE(controlVariable, 1);
+
+ static QSingleton<SingletonObject> s;
+ QTEST((int)s->val, "expectedValue");
+ s->val.ref();
+
+ QCOMPARE(SingletonObject::runCount, 1);
+}
+
+void tst_QThreadOnce::multipleThreads()
+{
+#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<SingletonObject> s;
+ s->val.ref();
+}
+
+void tst_QThreadOnce::reentering()
+{
+ const int WantedRecursions = 5;
+ int count = 0;
+ SingletonObject::runCount = 0;
+ reentrant(WantedRecursions, count);
+
+ // reentrancy is undefined behavior:
+ QVERIFY(count == 1 || count == WantedRecursions);
+ QCOMPARE(SingletonObject::runCount, 1);
+}
+
+#if !defined(QT_NO_EXCEPTIONS)
+static void exception_helper(int &val)
+{
+ Q_ONCE {
+ if (val++ == 0) throw 0;
+ }
+}
+#endif
+
+void tst_QThreadOnce::exception()
+{
+#if defined(QT_NO_EXCEPTIONS)
+ QSKIP("Compiled without exceptions, skipping test", SkipAll);
+#else
+ int count = 0;
+
+ try {
+ exception_helper(count);
+ } catch (...) {
+ // nothing
+ }
+ QCOMPARE(count, 1);
+
+ try {
+ exception_helper(count);
+ } catch (...) {
+ QVERIFY2(false, "Exception shouldn't have been thrown...");
+ }
+ QCOMPARE(count, 2);
+#endif
+}
+
+QTEST_MAIN(tst_QThreadOnce)
+#include "tst_qthreadonce.moc"
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 <QtCore/QtCore>
+
+class Class
+{
+public:
+ ~Class()
+ {
+ // trigger creation of a new QThreadStorage, after the previous QThreadStorage from main() was destructed
+ static QThreadStorage<int *> 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<int *> 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 <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qwaitcondition.h>
+#include <qthreadstorage.h>
+
+#ifdef Q_OS_UNIX
+#include <pthread.h>
+#endif
+#ifdef Q_OS_WIN
+#ifndef Q_OS_WINCE
+#include <process.h>
+#endif
+#include <windows.h>
+#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<Pointer *> pointers;
+ QVERIFY(!pointers.hasLocalData());
+ pointers.setLocalData(new Pointer);
+ QVERIFY(pointers.hasLocalData());
+ pointers.setLocalData(0);
+ QVERIFY(!pointers.hasLocalData());
+}
+
+void tst_QThreadStorage::localData()
+{
+ QThreadStorage<Pointer*> 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<Pointer *> pointers;
+ const QThreadStorage<Pointer *> &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<Pointer *> pointers;
+ QVERIFY(!pointers.hasLocalData());
+ pointers.setLocalData(new Pointer);
+ QVERIFY(pointers.hasLocalData());
+ pointers.setLocalData(0);
+ QVERIFY(!pointers.hasLocalData());
+}
+
+class Thread : public QThread
+{
+public:
+ QThreadStorage<Pointer *> &pointers;
+
+ QMutex mutex;
+ QWaitCondition cond;
+
+ Thread(QThreadStorage<Pointer *> &p)
+ : pointers(p)
+ { }
+
+ void run()
+ {
+ pointers.setLocalData(new Pointer);
+
+ QMutexLocker locker(&mutex);
+ cond.wakeOne();
+ cond.wait(&mutex);
+ }
+};
+
+void tst_QThreadStorage::autoDelete()
+{
+ QThreadStorage<Pointer *> 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<Pointer *> *pointers = reinterpret_cast<QThreadStorage<Pointer *> *>(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<Pointer *> 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 *> &first;
+ QThreadStorage<Second *> &second;
+
+ Thread(QThreadStorage<First *> &first,
+ QThreadStorage<Second *> &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 *> second;
+ QThreadStorage<First *> 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<SPointer *>, QTBUG14579_pointers1)
+Q_GLOBAL_STATIC(QThreadStorage<SPointer *>, 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<QTBUG14579_class *> &tls;
+
+ Thread(QThreadStorage<QTBUG14579_class *> &t) : tls(t) { }
+
+ void run()
+ {
+ QVERIFY(!tls.hasLocalData());
+ tls.setLocalData(new QTBUG14579_class);
+ QVERIFY(tls.hasLocalData());
+ }
+ };
+ int c = SPointer::count;
+
+ QThreadStorage<QTBUG14579_class *> tls;
+
+ QVERIFY(!QTBUG14579_pointers1()->hasLocalData());
+ QThreadStorage<int *> tls2; //add some more tls to make sure ids are not following each other too much
+ QThreadStorage<int *> 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_reset *>, QTBUG14579_resetTls)
+
+QTBUG14579_reset::~QTBUG14579_reset() {
+ //Quite stupid, but WTF::ThreadSpecific<T>::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<SPointer> &tlsSPointer;
+ QThreadStorage<QString> &tlsString;
+ QThreadStorage<int> &tlsInt;
+
+ int someNumber;
+ QString someString;
+ Thread(QThreadStorage<SPointer> &t1, QThreadStorage<QString> &t2, QThreadStorage<int> &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<SPointer> tlsSPointer;
+ QThreadStorage<QString> tlsString;
+ QThreadStorage<int> 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 <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qwaitcondition.h>
+
+#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 <QtTest/QtTest>
+
+#include <QCoreApplication>
+#include <QWriteLocker>
+#include <QSemaphore>
+#include <QThread>
+
+//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