EM_Task/CoreUObject/Private/UObject/LinkerManager.cpp

311 lines
10 KiB
C++
Raw Normal View History

2026-02-13 16:18:33 +08:00
// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
LinkerManager.h: Unreal object linker manager
=============================================================================*/
#include "UObject/LinkerManager.h"
#include "Internationalization/GatherableTextData.h"
#include "UObject/Package.h"
#include "UObject/ObjectResource.h"
#include "UObject/LinkerLoad.h"
#include "UObject/UObjectThreadContext.h"
#include "ProfilingDebugging/CsvProfiler.h"
FLinkerManager& FLinkerManager::Get()
{
static TUniquePtr<FLinkerManager> Singleton = MakeUnique<FLinkerManager>();
return *Singleton;
}
FLinkerManager::FLinkerManager(): bHasPendingCleanup(false)
{
}
FLinkerManager::~FLinkerManager()
{
}
bool FLinkerManager::Exec(class UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
{
#if !UE_BUILD_SHIPPING
if (FParse::Command(&Cmd, TEXT("LinkerLoadList")))
{
UE_LOG(LogLinker, Display, TEXT("ObjectLoaders: %d"), ObjectLoaders.Num());
for (auto Linker: ObjectLoaders)
{
UE_LOG(LogLinker, Display, TEXT("%s"), *Linker->Filename);
}
UE_LOG(LogLinker, Display, TEXT("LoadersWithNewImports: %d"), LoadersWithNewImports.Num());
for (auto Linker: LoadersWithNewImports)
{
UE_LOG(LogLinker, Display, TEXT("%s"), *Linker->Filename);
}
#if !UE_BUILD_SHIPPING && !UE_BUILD_TEST
UE_LOG(LogLinker, Display, TEXT("LiveLinkers: %d"), LiveLinkers.Num());
for (auto Linker: LiveLinkers)
{
UE_LOG(LogLinker, Display, TEXT("%s"), *Linker->Filename);
}
#endif
return true;
}
else if (FParse::Command(&Cmd, TEXT("LINKERS")))
{
Ar.Logf(TEXT("Linkers:"));
for (auto Linker: ObjectLoaders)
{
int32 NameSize = 0;
for (int32 j = 0; j < Linker->NameMap.Num(); j++)
{
if (FNameEntryId Id = Linker->NameMap[j])
{
NameSize += FName::GetEntry(Id)->GetSizeInBytes();
}
}
Ar.Logf(
TEXT("%s (%s): Names=%i (%iK/%iK) Text=%i (%iK) Imports=%i (%iK) Exports=%i (%iK) Gen=%i Bulk=%i"),
*Linker->Filename,
*Linker->LinkerRoot->GetFullName(),
Linker->NameMap.Num(),
Linker->NameMap.Num() * sizeof(FName) / 1024,
NameSize / 1024,
Linker->GatherableTextDataMap.Num(),
Linker->GatherableTextDataMap.Num() * sizeof(FGatherableTextData) / 1024,
Linker->ImportMap.Num(),
Linker->ImportMap.Num() * sizeof(FObjectImport) / 1024,
Linker->ExportMap.Num(),
Linker->ExportMap.Num() * sizeof(FObjectExport) / 1024,
Linker->Summary.Generations.Num(),
#if WITH_EDITOR
Linker->BulkDataLoaders.Num()
#else
0
#endif // WITH_EDITOR
);
}
return true;
}
#endif // !UE_BUILD_SHIPPING
return false;
}
void FLinkerManager::ResetLoaders(UObject* InPkg)
{
// Top level package to reset loaders for.
UObject* TopLevelPackage = InPkg ? InPkg->GetOutermost() : NULL;
// Find loader/ linker associated with toplevel package. We do this upfront as Detach resets LinkerRoot.
if (TopLevelPackage)
{
// Linker to reset/ detach.
auto LinkerToReset = FLinkerLoad::FindExistingLinkerForPackage(CastChecked<UPackage>(TopLevelPackage));
if (LinkerToReset)
{
{
#if THREADSAFE_UOBJECTS
FScopeLock ObjectLoadersLock(&ObjectLoadersCritical);
#endif
for (auto Linker: ObjectLoaders)
{
// Detach LinkerToReset from other linker's import table.
if (Linker->LinkerRoot != TopLevelPackage)
{
for (auto& Import: Linker->ImportMap)
{
if (Import.SourceLinker == LinkerToReset)
{
Import.SourceLinker = NULL;
Import.SourceIndex = INDEX_NONE;
}
}
}
else
{
check(Linker == LinkerToReset);
}
}
}
// Detach linker, also removes from array and sets LinkerRoot to NULL.
LinkerToReset->LoadAndDetachAllBulkData();
LinkerToReset->Detach();
RemoveLinker(LinkerToReset);
LinkerToReset = nullptr;
}
}
else
{
// We just want a copy here
TSet<FLinkerLoad*> LinkersToDetach;
GetLoaders(LinkersToDetach);
for (auto Linker: LinkersToDetach)
{
// Detach linker, also removes from array and sets LinkerRoot to NULL.
Linker->LoadAndDetachAllBulkData();
Linker->Detach();
RemoveLinker(Linker);
}
}
}
void FLinkerManager::ResetLoaders(const TSet<FLinkerLoad*>& InLinkerLoads)
{
// Remove import references
{
#if THREADSAFE_UOBJECTS
FScopeLock ObjectLoadersLock(&ObjectLoadersCritical);
#endif
for (FLinkerLoad* Linker: ObjectLoaders)
{
// Detach LinkerToReset from other linker's import table.
if (!InLinkerLoads.Contains(Linker))
{
for (auto& Import: Linker->ImportMap)
{
if (InLinkerLoads.Contains(Import.SourceLinker))
{
Import.SourceLinker = NULL;
Import.SourceIndex = INDEX_NONE;
}
}
}
}
}
for (FLinkerLoad* LinkerToReset: InLinkerLoads)
{
// Detach linker, also removes from array and sets LinkerRoot to NULL.
LinkerToReset->LoadAndDetachAllBulkData();
LinkerToReset->Detach();
}
// Remove all linkers in the specified set
{
#if THREADSAFE_UOBJECTS
FScopeLock PendingCleanupListLock(&PendingCleanupListCritical);
#endif
PendingCleanupList.Append(InLinkerLoads.Array());
bHasPendingCleanup = true;
}
}
void FLinkerManager::EnsureLoadingComplete(UPackage* Package)
{
if (!Package)
{
return;
}
FLinkerLoad* Linker = FLinkerLoad::FindExistingLinkerForPackage(Package);
if (!Linker)
{
return;
}
if (!Package->HasAnyPackageFlags(PKG_FilterEditorOnly))
{
Linker->SerializeThumbnails();
}
}
void FLinkerManager::DissociateImportsAndForcedExports()
{
{
// In cooked builds linkers don't stick around long enough to make this worthwhile
TSet<FLinkerLoad*> LocalLoadersWithNewImports;
GetLoadersWithNewImportsAndEmpty(LocalLoadersWithNewImports);
for (FLinkerLoad* Linker: LocalLoadersWithNewImports)
{
for (int32 ImportIndex = 0; ImportIndex < Linker->ImportMap.Num(); ImportIndex++)
{
FObjectImport& Import = Linker->ImportMap[ImportIndex];
// The import object could be stale if it has been replaced by patching
// logic or compile on load..
bool bIsStale = false;
if (Import.SourceLinker && Import.SourceIndex != INDEX_NONE)
{
bIsStale = Import.SourceLinker->ExportMap[Import.SourceIndex].Object != Import.XObject;
}
if (bIsStale || (Import.XObject && !Import.XObject->IsNative()))
{
Import.XObject = nullptr;
}
Import.SourceLinker = nullptr;
// when the SourceLinker is reset, the SourceIndex must also be reset, or recreating
// an import that points to a redirector will fail to find the redirector
Import.SourceIndex = INDEX_NONE;
}
if (Linker->GetSerializeContext())
{
Linker->GetSerializeContext()->ResetImportCount();
}
}
}
{
TSet<FLinkerLoad*> LocalLoadersWithForcedExports;
GetLoadersWithForcedExportsAndEmpty(LocalLoadersWithForcedExports);
for (FLinkerLoad* Linker: LocalLoadersWithForcedExports)
{
for (FObjectExport& Export: Linker->ExportMap)
{
if (Export.Object && Export.bForcedExport)
{
Export.Object->SetLinker(nullptr, INDEX_NONE);
Export.ResetObject();
}
}
if (Linker->GetSerializeContext())
{
Linker->GetSerializeContext()->ResetForcedExports();
}
}
}
}
void FLinkerManager::DeleteLinkers()
{
check(IsInGameThread());
if (bHasPendingCleanup)
{
bHasPendingCleanup = false;
QUICK_SCOPE_CYCLE_COUNTER(STAT_FLinkerManager_DeleteLinkers);
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(DeleteLinkers);
TArray<FLinkerLoad*> CleanupArray;
{
#if THREADSAFE_UOBJECTS
FScopeLock PendingCleanupListLock(&PendingCleanupListCritical);
#endif
CleanupArray = PendingCleanupList.Array();
PendingCleanupList.Empty();
}
// Note that even though DeleteLinkers can only be called on the main thread,
// we store the IsDeletingLinkers in TLS so that we're sure nothing on
// another thread can delete linkers except FLinkerManager at the time
// we enter this loop.
FUObjectThreadContext& ThreadContext = FUObjectThreadContext::Get();
ThreadContext.IsDeletingLinkers = true;
for (FLinkerLoad* Linker: CleanupArray)
{
delete Linker;
}
ThreadContext.IsDeletingLinkers = false;
}
}
void FLinkerManager::RemoveLinker(FLinkerLoad* Linker)
{
#if THREADSAFE_UOBJECTS
FScopeLock PendingCleanupListLock(&PendingCleanupListCritical);
#endif
if (Linker && !PendingCleanupList.Contains(Linker))
{
PendingCleanupList.Add(Linker);
bHasPendingCleanup = true;
}
}