diff options
Diffstat (limited to 'tests/auto/other/qobjectrace/tst_qobjectrace.cpp')
-rw-r--r-- | tests/auto/other/qobjectrace/tst_qobjectrace.cpp | 130 |
1 files changed, 76 insertions, 54 deletions
diff --git a/tests/auto/other/qobjectrace/tst_qobjectrace.cpp b/tests/auto/other/qobjectrace/tst_qobjectrace.cpp index ac33fa3ec3..af6634f253 100644 --- a/tests/auto/other/qobjectrace/tst_qobjectrace.cpp +++ b/tests/auto/other/qobjectrace/tst_qobjectrace.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore> @@ -32,6 +7,8 @@ #include <QtTest/private/qemulationdetector_p.h> +#include <optional> + enum { OneMinute = 60 * 1000, TwoMinutes = OneMinute * 2 }; @@ -44,11 +21,20 @@ struct Functor class tst_QObjectRace: public QObject { Q_OBJECT +public: + tst_QObjectRace() + : ThreadCount(QThread::idealThreadCount()) + {} + private slots: void moveToThreadRace(); void destroyRace(); void blockingQueuedDestroyRace(); void disconnectRace(); + void disconnectRace2(); + +private: + const int ThreadCount; }; class RaceObject : public QObject @@ -132,8 +118,7 @@ void tst_QObjectRace::moveToThreadRace() { RaceObject *object = new RaceObject; - enum { ThreadCount = 6 }; - RaceThread *threads[ThreadCount]; + QVarLengthArray<RaceThread *, 16> threads(ThreadCount); for (int i = 0; i < ThreadCount; ++i) { threads[i] = new RaceThread; threads[i]->setObject(object); @@ -263,10 +248,10 @@ void tst_QObjectRace::destroyRace() if (QTestPrivate::isRunningArmOnX86()) QSKIP("Test is too slow to run on emulator"); - enum { ThreadCount = 10, ObjectCountPerThread = 2777, - ObjectCount = ThreadCount * ObjectCountPerThread }; + constexpr int ObjectCountPerThread = 2777; + const int ObjectCount = ThreadCount * ObjectCountPerThread; - MyObject *objects[ObjectCount]; + QVarLengthArray<MyObject *, ObjectCountPerThread * 10> objects(ObjectCount); for (int i = 0; i < ObjectCount; ++i) objects[i] = new MyObject; @@ -283,10 +268,10 @@ void tst_QObjectRace::destroyRace() objects[((i+5)*79) % ObjectCount], Functor() ); } - DestroyThread *threads[ThreadCount]; + QVarLengthArray<DestroyThread *, 16> threads(ThreadCount); for (int i = 0; i < ThreadCount; ++i) { threads[i] = new DestroyThread; - threads[i]->setObjects(objects + i*ObjectCountPerThread, ObjectCountPerThread); + threads[i]->setObjects(objects.data() + i*ObjectCountPerThread, ObjectCountPerThread); } for (int i = 0; i < ThreadCount; ++i) @@ -324,8 +309,7 @@ public slots: break; } - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } private: @@ -334,9 +318,6 @@ private: void tst_QObjectRace::blockingQueuedDestroyRace() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else enum { MinIterations = 100, MinTime = 3000, WaitTime = 25 }; BlockingQueuedDestroyRaceObject sender; @@ -346,17 +327,13 @@ void tst_QObjectRace::blockingQueuedDestroyRace() while (iteration++ < MinIterations || !timer.hasExpired()) { // Manually allocate some storage, and create a receiver in there - std::aligned_storage< - sizeof(BlockingQueuedDestroyRaceObject), - alignof(BlockingQueuedDestroyRaceObject) - >::type storage; + std::optional<BlockingQueuedDestroyRaceObject> receiver; - auto *receiver = reinterpret_cast<BlockingQueuedDestroyRaceObject *>(&storage); - new (receiver) BlockingQueuedDestroyRaceObject(BlockingQueuedDestroyRaceObject::Behavior::Normal); + receiver.emplace(BlockingQueuedDestroyRaceObject::Behavior::Normal); // Connect it to the sender via BlockingQueuedConnection QVERIFY(connect(&sender, &BlockingQueuedDestroyRaceObject::aSignal, - receiver, &BlockingQueuedDestroyRaceObject::aSlot, + &*receiver, &BlockingQueuedDestroyRaceObject::aSlot, Qt::BlockingQueuedConnection)); const auto emitUntilDestroyed = [&sender] { @@ -378,17 +355,14 @@ void tst_QObjectRace::blockingQueuedDestroyRace() // - the metacall event to be posted to a destroyed object; // - the metacall event to be posted to the wrong object. // In both cases we hope to catch the race by crashing. - receiver->~BlockingQueuedDestroyRaceObject(); - new (receiver) BlockingQueuedDestroyRaceObject(BlockingQueuedDestroyRaceObject::Behavior::Crash); + receiver.reset(); + receiver.emplace(BlockingQueuedDestroyRaceObject::Behavior::Crash); // Flush events QTest::qWait(0); thread->wait(); - - receiver->~BlockingQueuedDestroyRaceObject(); } -#endif } static QAtomicInteger<unsigned> countedStructObjectsCount; @@ -505,7 +479,7 @@ public: void tst_QObjectRace::disconnectRace() { - enum { ThreadCount = 20, TimeLimit = 3000 }; + enum { TimeLimit = 3000 }; QCOMPARE(countedStructObjectsCount.loadRelaxed(), 0u); @@ -515,7 +489,7 @@ void tst_QObjectRace::disconnectRace() senderThread->start(); sender->moveToThread(senderThread.data()); - DisconnectRaceThread *threads[ThreadCount]; + QVarLengthArray<DisconnectRaceThread *, 16> threads(ThreadCount); for (int i = 0; i < ThreadCount; ++i) { threads[i] = new DisconnectRaceThread(sender.data(), !(i % 10)); threads[i]->start(); @@ -541,7 +515,7 @@ void tst_QObjectRace::disconnectRace() senderThread->start(); sender->moveToThread(senderThread.data()); - DeleteReceiverRaceReceiverThread *threads[ThreadCount]; + QVarLengthArray<DeleteReceiverRaceReceiverThread *, 16> threads(ThreadCount); for (int i = 0; i < ThreadCount; ++i) { threads[i] = new DeleteReceiverRaceReceiverThread(sender.data()); threads[i]->start(); @@ -562,5 +536,53 @@ void tst_QObjectRace::disconnectRace() QCOMPARE(countedStructObjectsCount.loadRelaxed(), 0u); } +void tst_QObjectRace::disconnectRace2() +{ + enum { IterationCount = 100, ConnectionCount = 100, YieldCount = 100 }; + + QAtomicPointer<MyObject> ptr; + QSemaphore createSemaphore(0); + QSemaphore proceedSemaphore(0); + + std::unique_ptr<QThread> t1(QThread::create([&]() { + for (int i = 0; i < IterationCount; ++i) { + MyObject sender; + ptr.storeRelease(&sender); + createSemaphore.release(); + proceedSemaphore.acquire(); + ptr.storeRelaxed(nullptr); + for (int i = 0; i < YieldCount; ++i) + QThread::yieldCurrentThread(); + } + })); + t1->start(); + + + std::unique_ptr<QThread> t2(QThread::create([&]() { + auto connections = std::make_unique<QMetaObject::Connection[]>(ConnectionCount); + for (int i = 0; i < IterationCount; ++i) { + MyObject receiver; + MyObject *sender = nullptr; + + createSemaphore.acquire(); + + while (!(sender = ptr.loadAcquire())) + ; + + for (int i = 0; i < ConnectionCount; ++i) + connections[i] = QObject::connect(sender, &MyObject::signal1, &receiver, &MyObject::slot1); + + proceedSemaphore.release(); + + for (int i = 0; i < ConnectionCount; ++i) + QObject::disconnect(connections[i]); + } + })); + t2->start(); + + t1->wait(); + t2->wait(); +} + QTEST_MAIN(tst_QObjectRace) #include "tst_qobjectrace.moc" |