// Copyright Epic Games, Inc. All Rights Reserved. #include "Catch2Includes.h" #include "AutoRTFM.h" #include "AutoRTFMTesting.h" #include "AutoRTFMTestUtils.h" #include "Misc/TransactionallySafeRWLock.h" #include "Misc/ScopeRWLock.h" #include #include // Helper class for TEMPLATE_TEST_CASE, which expects a type but we want to pass a value. template struct TRWLockTypeHolder { static constexpr FRWScopeLockType Value = T; }; TEMPLATE_TEST_CASE("TransactionallySafeRWLock.RWScopeLock", "[ScopeLock]", TRWLockTypeHolder, TRWLockTypeHolder) { SECTION("Outside Transaction With Lock") { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Abort([&] { UE::TRWScopeLock Lock(RWLock, TestType::Value); AutoRTFM::AbortTransaction(); }); AutoRTFM::Testing::Commit([&] { UE::TRWScopeLock Lock(RWLock, TestType::Value); }); } SECTION("Inside Transaction With Lock") { AutoRTFM::Testing::Abort([&] { FTransactionallySafeRWLock RWLock; UE::TRWScopeLock Lock(RWLock, TestType::Value); AutoRTFM::AbortTransaction(); }); AutoRTFM::Testing::Commit([&] { FTransactionallySafeRWLock RWLock; UE::TRWScopeLock Lock(RWLock, TestType::Value); }); } } TEMPLATE_TEST_CASE("TransactionallySafeRWLock.NestedRWScopeLock", "[ScopeLock]", TRWLockTypeHolder, TRWLockTypeHolder) { SECTION("Abort(Abort(Lock))") { AutoRTFM::Testing::Abort([&] { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Abort([&] { UE::TRWScopeLock Lock(RWLock, TestType::Value); AutoRTFM::CascadingAbortTransaction(); }); }); } SECTION("Commit(Abort(Lock))") { AutoRTFM::Testing::Commit([&] { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Abort([&] { UE::TRWScopeLock Lock(RWLock, TestType::Value); AutoRTFM::AbortTransaction(); }); }); } SECTION("Abort(Commit(Lock))") { AutoRTFM::Testing::Abort([&] { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Commit([&] { UE::TRWScopeLock Lock(RWLock, TestType::Value); }); AutoRTFM::AbortTransaction(); }); } SECTION("Commit(Commit(Lock))") { AutoRTFM::Testing::Commit([&] { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Commit([&] { UE::TRWScopeLock Lock(RWLock, TestType::Value); }); }); } } TEMPLATE_TEST_CASE("TransactionallySafeRWLock.ScopeLock", "[ScopeLock]", UE::TReadScopeLock, UE::TWriteScopeLock) { SECTION("Outside Transaction") { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Abort([&] { TestType Lock(RWLock); AutoRTFM::AbortTransaction(); }); AutoRTFM::Testing::Commit([&] { TestType Lock(RWLock); }); } SECTION("Inside Transaction") { AutoRTFM::Testing::Abort([&] { FTransactionallySafeRWLock RWLock; TestType Lock(RWLock); AutoRTFM::AbortTransaction(); }); AutoRTFM::Testing::Commit([&] { FTransactionallySafeRWLock RWLock; TestType Lock(RWLock); }); } } TEMPLATE_TEST_CASE("TransactionallySafeRWLock.NestedScopeLock", "[ScopeLock]", UE::TReadScopeLock, UE::TWriteScopeLock) { SECTION("Abort(Abort(Lock))") { AutoRTFM::Testing::Abort([&] { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Abort([&] { TestType Lock(RWLock); AutoRTFM::CascadingAbortTransaction(); }); }); } SECTION("Commit(Abort(Lock))") { AutoRTFM::Testing::Commit([&] { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Abort([&] { TestType Lock(RWLock); AutoRTFM::AbortTransaction(); }); }); } SECTION("Abort(Commit(Lock))") { AutoRTFM::Testing::Abort([&] { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Commit([&] { TestType Lock(RWLock); }); AutoRTFM::AbortTransaction(); }); } SECTION("Commit(Commit(Lock))") { AutoRTFM::Testing::Commit([&] { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Commit([&] { TestType Lock(RWLock); }); }); } } TEMPLATE_TEST_CASE("TransactionallySafeRWLock.ScopeLock.FailedTryWriteInTransaction", "[ScopeLock]", UE::TReadScopeLock, UE::TWriteScopeLock) { FTransactionallySafeRWLock RWLock; TestType Lock(RWLock); SECTION("Abort") { AutoRTFM::Testing::Abort([&] { REQUIRE(!RWLock.TryWriteLock()); AutoRTFM::CascadingAbortTransaction(); }); } SECTION("Commit") { AutoRTFM::Testing::Commit([&] { REQUIRE(!RWLock.TryWriteLock()); }); } } TEMPLATE_TEST_CASE("TransactionallySafeRWLock.ZeroAndReconstruct", "[ScopeLock]", UE::TReadScopeLock, UE::TWriteScopeLock) { SECTION("Lock, destruct, memzero, reconstruct") { FTransactionallySafeRWLock RWLock; SECTION("Commit") { AutoRTFM::Testing::Commit([&] { { // Lock and then unlock TestType Lock(RWLock); } RWLock.~FTransactionallySafeRWLock(); memset(&RWLock, 0, sizeof(RWLock)); new (&RWLock) FTransactionallySafeRWLock(); }); } SECTION("Abort") { AutoRTFM::Testing::Abort([&] { { // Lock and then unlock TestType Lock(RWLock); } RWLock.~FTransactionallySafeRWLock(); memset(&RWLock, 0, sizeof(RWLock)); new (&RWLock) FTransactionallySafeRWLock(); AutoRTFM::AbortTransaction(); }); } } } TEST_CASE("TransactionallySafeRWLock.TryWriteLock") { SECTION("Declared Outside Transaction") { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Abort([&] { REQUIRE(RWLock.TryWriteLock()); AutoRTFM::AbortTransaction(); }); AutoRTFM::Testing::Commit([&] { REQUIRE(RWLock.TryWriteLock()); RWLock.WriteUnlock(); }); } SECTION("Declared Inside Transaction") { AutoRTFM::Testing::Abort([&] { FTransactionallySafeRWLock RWLock; REQUIRE(RWLock.TryWriteLock()); AutoRTFM::AbortTransaction(); }); AutoRTFM::Testing::Commit([&] { FTransactionallySafeRWLock RWLock; REQUIRE(RWLock.TryWriteLock()); RWLock.WriteUnlock(); }); } } TEST_CASE("TransactionallySafeRWLock.NestedTryWriteLock") { SECTION("Abort(Abort(TryWriteLock))") { AutoRTFM::Testing::Abort([&] { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Abort([&] { REQUIRE(RWLock.TryWriteLock()); AutoRTFM::CascadingAbortTransaction(); }); }); } SECTION("Commit(Abort(TryWriteLock))") { AutoRTFM::Testing::Commit([&] { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Abort([&] { REQUIRE(RWLock.TryWriteLock()); AutoRTFM::AbortTransaction(); }); }); } SECTION("Abort(Commit(TryWriteLock))") { AutoRTFM::Testing::Abort([&] { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Commit([&] { REQUIRE(RWLock.TryWriteLock()); }); AutoRTFM::AbortTransaction(); }); } SECTION("Commit(Commit(TryWriteLock))") { AutoRTFM::Testing::Commit([&] { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Commit([&] { REQUIRE(RWLock.TryWriteLock()); }); RWLock.WriteUnlock(); }); } } TEMPLATE_TEST_CASE("TransactionallySafeRWLock.RWScopeLock.ScopeLockThenTryWriteLock", "[ScopeLock]", UE::TReadScopeLock, UE::TWriteScopeLock) { SECTION("Lock, Abort(TryWrite)") { FTransactionallySafeRWLock RWLock; TestType Lock(RWLock); AutoRTFM::Testing::Abort([&] { REQUIRE(!RWLock.TryWriteLock()); AutoRTFM::CascadingAbortTransaction(); }); } SECTION("Lock, Commit(TryWrite)") { FTransactionallySafeRWLock RWLock; TestType Lock(RWLock); AutoRTFM::Testing::Commit([&] { REQUIRE(!RWLock.TryWriteLock()); }); } } TEST_CASE("TransactionallySafeRWLock.MultipleTryWriteLocks") { SECTION("TryWrite, TryWrite, Unlock") { FTransactionallySafeRWLock RWLock; REQUIRE(RWLock.TryWriteLock()); REQUIRE(!RWLock.TryWriteLock()); RWLock.WriteUnlock(); } SECTION("TryWrite, Commit(TryWrite), Unlock") { FTransactionallySafeRWLock RWLock; REQUIRE(RWLock.TryWriteLock()); AutoRTFM::Testing::Commit([&] { REQUIRE(!RWLock.TryWriteLock()); }); RWLock.WriteUnlock(); } SECTION("TryWrite, Abort(TryWrite), Unlock") { FTransactionallySafeRWLock RWLock; REQUIRE(RWLock.TryWriteLock()); AutoRTFM::Testing::Abort([&] { REQUIRE(!RWLock.TryWriteLock()); AutoRTFM::AbortTransaction(); }); RWLock.WriteUnlock(); } // Test case does not pass. RWLock cannot be locked in the open and unlocked in the closed: #jira SOL-7661 SECTION("TryWrite, Commit(TryWrite, Unlock)") { // FTransactionallySafeRWLock RWLock; // REQUIRE(RWLock.TryWriteLock()); // AutoRTFM::Testing::Commit([&] // { // REQUIRE(!RWLock.TryWriteLock()); // RWLock.WriteUnlock(); // }); } // Test case does not pass. RWLock cannot be locked in the open and unlocked in the closed: #jira SOL-7661 SECTION("TryWrite, Abort(TryWrite, Unlock)") { // FTransactionallySafeRWLock RWLock; // REQUIRE(RWLock.TryWriteLock()); // AutoRTFM::Testing::Abort([&] // { // REQUIRE(!RWLock.TryWriteLock()); // RWLock.WriteUnlock(); // AutoRTFM::AbortTransaction(); // }); // RWLock.WriteUnlock(); } // Test case does not pass. RWLock cannot be locked in the closed and unlocked in the open: #jira SOL-7661 SECTION("Commit(TryWrite, TryWrite), Unlock") { // FTransactionallySafeRWLock RWLock; // AutoRTFM::Testing::Commit([&] // { // REQUIRE(RWLock.TryWriteLock()); // REQUIRE(!RWLock.TryWriteLock()); // }); // RWLock.WriteUnlock(); } SECTION("Abort(TryWrite, TryWrite)") { FTransactionallySafeRWLock RWLock; AutoRTFM::Testing::Abort([&] { REQUIRE(RWLock.TryWriteLock()); REQUIRE(!RWLock.TryWriteLock()); AutoRTFM::AbortTransaction(); }); } SECTION("Commit(TryWrite, TryWrite, Unlock)") { AutoRTFM::Testing::Commit([&] { FTransactionallySafeRWLock RWLock; REQUIRE(RWLock.TryWriteLock()); REQUIRE(!RWLock.TryWriteLock()); RWLock.WriteUnlock(); }); } SECTION("Abort(TryWrite, TryWrite, Unlock)") { AutoRTFM::Testing::Abort([&] { FTransactionallySafeRWLock RWLock; REQUIRE(RWLock.TryWriteLock()); REQUIRE(!RWLock.TryWriteLock()); RWLock.WriteUnlock(); AutoRTFM::AbortTransaction(); }); } }