// 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"