You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
The goal here is that if a UObject is constructed during a transaction, and then decides to abort, we leave the UObject in an intact-enough state that the GC can come along later to destruct and deallocate the object. To that end, we move StaticConstructObject_Internal back to instrumented, but execute the UObject malloc and UObjectBase constructor uninstrumented. Then later when the derived constructor runs over the same memory, we run the constructor instrumented, except when it gets down to the UObject constructor level, we run that uninstrumented. If we abort, the UObject memory will not be freed by the AutoRTFM runtime, the destructor will not be called, but the memory will be rolled back to what the UObject and base constructors initialize it as, so it should be a bare bones object ready for destruction and deallocation. - added a new attribute to the compiler (autortm_always_open) so we can annotate functions themselves (like constructors) to ensure the entire function isn't instrumented (including code we can't normally wrap, like when the constructor writes the vtable pointer) #rb Brandon.Schaefer, neil.henning [CL 32426570 by michael nicolella in ue5-main branch]
138 lines
3.3 KiB
C++
138 lines
3.3 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MyAutoRTFMTestObject.h"
|
|
#include "Catch2Includes.h"
|
|
#include <AutoRTFM/AutoRTFM.h>
|
|
#include "UObject/GCObject.h"
|
|
#include "UObject/ReachabilityAnalysis.h"
|
|
|
|
TEST_CASE("UObject.NewObject")
|
|
{
|
|
SECTION("Create")
|
|
{
|
|
UMyAutoRTFMTestObject* Object = nullptr;
|
|
|
|
AutoRTFM::Commit([&]
|
|
{
|
|
Object = NewObject<UMyAutoRTFMTestObject>();
|
|
});
|
|
|
|
REQUIRE(nullptr != Object);
|
|
REQUIRE(42 == Object->Value);
|
|
}
|
|
|
|
SECTION("Abort")
|
|
{
|
|
UMyAutoRTFMTestObject* Object = nullptr;
|
|
|
|
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == AutoRTFM::Transact([&]
|
|
{
|
|
Object = NewObject<UMyAutoRTFMTestObject>();
|
|
AutoRTFM::AbortTransaction();
|
|
}));
|
|
|
|
REQUIRE(nullptr == Object);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("UObject.NewObjectWithOuter")
|
|
{
|
|
SECTION("Create")
|
|
{
|
|
UMyAutoRTFMTestObject* Outer = NewObject<UMyAutoRTFMTestObject>();
|
|
UMyAutoRTFMTestObject* Object = nullptr;
|
|
|
|
AutoRTFM::Commit([&]
|
|
{
|
|
Object = NewObject<UMyAutoRTFMTestObject>(Outer);
|
|
});
|
|
|
|
REQUIRE(nullptr != Object);
|
|
REQUIRE(42 == Object->Value);
|
|
REQUIRE(Object->IsInOuter(Outer));
|
|
REQUIRE(55 == Outer->Value);
|
|
}
|
|
|
|
SECTION("Abort")
|
|
{
|
|
UMyAutoRTFMTestObject* Outer = NewObject<UMyAutoRTFMTestObject>();
|
|
UMyAutoRTFMTestObject* Object = nullptr;
|
|
|
|
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == AutoRTFM::Transact([&]
|
|
{
|
|
Object = NewObject<UMyAutoRTFMTestObject>(Outer);
|
|
AutoRTFM::AbortTransaction();
|
|
}));
|
|
|
|
REQUIRE(nullptr == Object);
|
|
REQUIRE(42 == Outer->Value);
|
|
}
|
|
}
|
|
|
|
// This is a copy of the helper function in TestGarbageCollector.cpp.
|
|
int32 PerformGarbageCollectionWithIncrementalReachabilityAnalysis(TFunctionRef<bool(int32)> ReachabilityIterationCallback)
|
|
{
|
|
int32 ReachabilityIterationIndex = 0;
|
|
|
|
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS, false);
|
|
|
|
while (IsIncrementalReachabilityAnalysisPending())
|
|
{
|
|
if (ReachabilityIterationCallback(ReachabilityIterationIndex))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Re-check if incremental rachability is still pending because the callback above could've triggered GC which would complete all iterations
|
|
if (IsIncrementalReachabilityAnalysisPending())
|
|
{
|
|
PerformIncrementalReachabilityAnalysis(GetReachabilityAnalysisTimeLimit());
|
|
ReachabilityIterationIndex++;
|
|
}
|
|
}
|
|
|
|
if (IsIncrementalPurgePending())
|
|
{
|
|
IncrementalPurgeGarbage(false);
|
|
}
|
|
check(IsIncrementalPurgePending() == false);
|
|
|
|
return ReachabilityIterationIndex + 1;
|
|
}
|
|
|
|
TEST_CASE("UObject.MarkAsReachable")
|
|
{
|
|
// We need incremental reachability to be on.
|
|
SetIncrementalReachabilityAnalysisEnabled(true);
|
|
|
|
// Cache the original time limit.
|
|
const float Original = GetReachabilityAnalysisTimeLimit();
|
|
|
|
// And we need a super small time limit s that reachability analysis will definitely have started.
|
|
SetReachabilityAnalysisTimeLimit(FLT_MIN);
|
|
|
|
// We need to be sure we've done the static GC initialization before we start doing a garbage
|
|
// collection.
|
|
FGCObject::StaticInit();
|
|
|
|
UMyAutoRTFMTestObject* const Object = NewObject<UMyAutoRTFMTestObject>();
|
|
|
|
PerformGarbageCollectionWithIncrementalReachabilityAnalysis([Object](int32 index)
|
|
{
|
|
if (0 != index)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
AutoRTFM::Commit([&]
|
|
{
|
|
Object->MarkAsReachable();
|
|
});
|
|
|
|
return false;
|
|
});
|
|
|
|
// Reset it back just incase another test required the original time limit.
|
|
SetReachabilityAnalysisTimeLimit(Original);
|
|
}
|