From 5e48a51608485cbc87b68498fd54ab36f001d584 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 30 May 2022 13:48:52 +0200 Subject: Long live QAtomicScopedValueRollback (private API)! QScopedValueRollback has a few users that apply it on QAtomicInt, which happens to work as QAtomicInt is copy-constructible and its ctors are implicit. But that's of course nonsense. We don't need to store the oldValue in an atomic, nor do we need to pass the new value into the ctor as an atomic. So, add a QAtomicScopedValueRollback which works on std::atomic as well as the Qt atomics, but distinguishes between the reference (which is atomic) and the value (which isn't), and use it in one of the users, tst_QList. Keep it private until we know whether there's an actual need for this. The test is a copy of tst_qscopedvaluefallback, so the occasional oddity (like atomic op*=) should be ignored. Task-number: QTBUG-103835 Change-Id: I3c05b3e51f465698657a02ca5521ed465386e9a6 Reviewed-by: Qt CI Bot Reviewed-by: Fabian Kosmale --- tests/auto/corelib/tools/CMakeLists.txt | 1 + .../qatomicscopedvaluerollback/CMakeLists.txt | 6 + .../tst_qatomicscopedvaluerollback.cpp | 157 +++++++++++++++++++++ tests/auto/corelib/tools/qlist/CMakeLists.txt | 2 + tests/auto/corelib/tools/qlist/tst_qlist.cpp | 6 +- 5 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 tests/auto/corelib/tools/qatomicscopedvaluerollback/CMakeLists.txt create mode 100644 tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp (limited to 'tests/auto/corelib/tools') diff --git a/tests/auto/corelib/tools/CMakeLists.txt b/tests/auto/corelib/tools/CMakeLists.txt index 185a3e1bcc..d238219485 100644 --- a/tests/auto/corelib/tools/CMakeLists.txt +++ b/tests/auto/corelib/tools/CMakeLists.txt @@ -1,4 +1,5 @@ # Generated from tools.pro. +add_subdirectory(qatomicscopedvaluerollback) if(NOT INTEGRITY) add_subdirectory(collections) endif() diff --git a/tests/auto/corelib/tools/qatomicscopedvaluerollback/CMakeLists.txt b/tests/auto/corelib/tools/qatomicscopedvaluerollback/CMakeLists.txt new file mode 100644 index 0000000000..3ba7cb9a6b --- /dev/null +++ b/tests/auto/corelib/tools/qatomicscopedvaluerollback/CMakeLists.txt @@ -0,0 +1,6 @@ +qt_internal_add_test(tst_qatomicscopedvaluerollback + SOURCES + tst_qatomicscopedvaluerollback.cpp + PUBLIC_LIBRARIES + Qt::CorePrivate +) diff --git a/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp b/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp new file mode 100644 index 0000000000..81d8242f71 --- /dev/null +++ b/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp @@ -0,0 +1,157 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include + +#include + +class tst_QAtomicScopedValueRollback : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void leavingScope(); + void leavingScopeAfterCommit(); + void rollbackToPreviousCommit(); + void exceptions(); + void earlyExitScope(); +private: + void earlyExitScope_helper(int exitpoint, std::atomic &member); +}; + +void tst_QAtomicScopedValueRollback::leavingScope() +{ + QAtomicInt i = 0; + QBasicAtomicInteger b = false; + std::atomic b2 = false; + + //test rollback on going out of scope + { + QAtomicScopedValueRollback ri(i); + QAtomicScopedValueRollback rb(b); + QAtomicScopedValueRollback rb2(b2, true); + QCOMPARE(b.loadRelaxed(), false); + QCOMPARE(b2, true); + QCOMPARE(i.loadRelaxed(), 0); + b.storeRelaxed(true); + i.storeRelaxed(1); + QCOMPARE(b.loadRelaxed(), true); + QCOMPARE(i.loadRelaxed(), 1); + } + QCOMPARE(b.loadRelaxed(), false); + QCOMPARE(b2, false); + QCOMPARE(i.loadRelaxed(), 0); +} + +void tst_QAtomicScopedValueRollback::leavingScopeAfterCommit() +{ + std::atomic i = 0; + QAtomicInteger b = false; + + //test rollback on going out of scope + { + QAtomicScopedValueRollback ri(i); + QAtomicScopedValueRollback rb(b); + QCOMPARE(b.loadRelaxed(), false); + QCOMPARE(i, 0); + b.storeRelaxed(true); + i = 1; + QCOMPARE(b.loadRelaxed(), true); + QCOMPARE(i, 1); + ri.commit(); + rb.commit(); + } + QCOMPARE(b.loadRelaxed(), true); + QCOMPARE(i, 1); +} + +void tst_QAtomicScopedValueRollback::rollbackToPreviousCommit() +{ + QBasicAtomicInt i = 0; + { + QAtomicScopedValueRollback ri(i); + i++; + ri.commit(); + i++; + } + QCOMPARE(i.loadRelaxed(), 1); + { + QAtomicScopedValueRollback ri1(i); + i++; + ri1.commit(); + i++; + ri1.commit(); + i++; + } + QCOMPARE(i.loadRelaxed(), 3); +} + +void tst_QAtomicScopedValueRollback::exceptions() +{ + std::atomic b = false; + bool caught = false; + QT_TRY + { + QAtomicScopedValueRollback rb(b); + b = true; + QT_THROW(std::bad_alloc()); //if Qt compiled without exceptions this is noop + rb.commit(); //if Qt compiled without exceptions, true is committed + } + QT_CATCH(...) + { + caught = true; + } + QCOMPARE(b, !caught); //expect false if exception was thrown, true otherwise +} + +void tst_QAtomicScopedValueRollback::earlyExitScope() +{ + QAtomicInt ai = 0; + std::atomic aj = 0; + while (true) { + QAtomicScopedValueRollback ri(ai); + ++ai; + aj = ai.loadRelaxed(); + if (ai.loadRelaxed() > 8) break; + ri.commit(); + } + QCOMPARE(ai.loadRelaxed(), 8); + QCOMPARE(aj.load(), 9); + + for (int i = 0; i < 5; ++i) { + aj = 1; + earlyExitScope_helper(i, aj); + QCOMPARE(aj.load(), 1 << i); + } +} + +static void operator*=(std::atomic &lhs, int rhs) +{ + int expected = lhs.load(); + while (!lhs.compare_exchange_weak(expected, expected * rhs)) + ; +} + +void tst_QAtomicScopedValueRollback::earlyExitScope_helper(int exitpoint, std::atomic& member) +{ + QAtomicScopedValueRollback r(member); + member *= 2; + if (exitpoint == 0) + return; + r.commit(); + member *= 2; + if (exitpoint == 1) + return; + r.commit(); + member *= 2; + if (exitpoint == 2) + return; + r.commit(); + member *= 2; + if (exitpoint == 3) + return; + r.commit(); +} + +QTEST_MAIN(tst_QAtomicScopedValueRollback) +#include "tst_qatomicscopedvaluerollback.moc" diff --git a/tests/auto/corelib/tools/qlist/CMakeLists.txt b/tests/auto/corelib/tools/qlist/CMakeLists.txt index 89b92ab305..46281a0db8 100644 --- a/tests/auto/corelib/tools/qlist/CMakeLists.txt +++ b/tests/auto/corelib/tools/qlist/CMakeLists.txt @@ -7,6 +7,8 @@ qt_internal_add_test(tst_qlist SOURCES tst_qlist.cpp + PUBLIC_LIBRARIES + Qt::CorePrivate ) ## Scopes: diff --git a/tests/auto/corelib/tools/qlist/tst_qlist.cpp b/tests/auto/corelib/tools/qlist/tst_qlist.cpp index fef30647e3..e797b76f69 100644 --- a/tests/auto/corelib/tools/qlist/tst_qlist.cpp +++ b/tests/auto/corelib/tools/qlist/tst_qlist.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include @@ -3181,7 +3181,7 @@ void tst_QList::emplaceReturnsIterator() void tst_QList::emplaceFront() const { - QScopedValueRollback rollback(Movable::counter, 0); + QAtomicScopedValueRollback rollback(Movable::counter, 0); QList vec; vec.emplaceFront('b'); @@ -3206,7 +3206,7 @@ void tst_QList::emplaceFrontReturnsRef() const void tst_QList::emplaceBack() { - QScopedValueRollback rollback(Movable::counter, 0); + QAtomicScopedValueRollback rollback(Movable::counter, 0); QList vec; -- cgit v1.2.3