How to avoid GC? (Threads)

I have this error when stopping the game in the editor:

Assertion failed: false [File:D:\build++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp] [Line: 517] Object ‘World /Game/Game/Maps/GamePlay/Arena01/UEDPIE_0_L_Arena01.L_Arena01’ from PIE level still referenced. Shortest path from root: (Garbage) (async) NavigationPath /Game/Game/Maps/GamePlay/Arena01/UEDPIE_0_L_Arena01.L_Arena01:NavigationSystemV1_0.NavigationPath_3674 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ This reference is preventing the old World from being GC’d ^ → UObject* UObject::Outer = (Garbage) NavigationSystemV1 /Game/Game/Maps/GamePlay/Arena01/UEDPIE_0_L_Arena01.L_Arena01:NavigationSystemV1_0 → UObject* UObject::Outer = (Garbage) World /Game/Game/Maps/GamePlay/Arena01/UEDPIE_0_L_Arena01.L_Arena01

I know it’s caused because I’m using a world reference inside a thread.

I don’t know if this is the best solution but if the GC waits just one second the thread will finish execution and release the reference.

Although if there is a better way to handle this problem I would like to be let me know please.

This is my first time using threads in Unreal… any help is welcome.

Thank you so much!!

Well, I thought if I can’t tell the GC to wait, maybe I can destroy the thread before collecting the garbage.

I have tried this.

	FCoreUObjectDelegates::GetPreGarbageCollectDelegate().AddWeakLambda
	(
		this,[this]()
		{
			Runnable->Stop();
			Runnable->Kill();		
		}
	);

And I have also tried this.

//This is used as if it was a PostPIEEnded event
EndPlayDelegateHandle = FGameDelegates::Get().GetEndPlayMapDelegate().AddUObject(this, &AEnemyAIController::OnMapChange, (uint32)0);


void AEnemyAIController::OnMapChange(uint32 MapChangeFlags)
{
	if (Runnable!=nullptr)
	{
		Runnable->Stop();
		Runnable->Kill();
	}	
}

But the garbage collector is faster… That is to say, it does not work.

An event or delegate that can be overwritten before the world is destroyed, does anyone know?

Thank you!!

I found that if I unmark the navigation system as garbage at the end of the task that executes the thread, the game does not crash and everything works perfectly.

void Task()
{
	UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(Controller->GetWorld());
	if (!IsValid(NavSys)) return;	

    // Some code here
	
	NavSys->ClearGarbage();
}

The question now is… should I delete the “NavSys” pointer manually?

And how would it be done correctly?

I suspect that freeing memory C++ style is not the right way to do it…

delete NavSys:

And style C neither, right?

free(NavSysy)

If someone can resolve this doubt for me, I think the job is done.

Thank you so much.

Ok, I think I found a solution… theoretically a smart pointer will free the memory for me.

TWeakObjectPtr<UNavigationSystemV1> NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(Controller->GetWorld());

Thanks Mikelis…

Anyway, I would like to know how to do it manually… I think it has not to be very complicated… the difficult thing is finding the information on how to do it.

Thank you so much!!

NavSys = nullptr;

Should be enough.
It will get cleaned up if put before NavSys->ClearGarbage();

Only use delete if you use the new command on an object this is not governed by Unreal’s lifecycle.

Also inside of threads best to use TSharedPtr or weak pointers that can invalidate automatically to null pointers.

1 Like

Brilliant!! Thank you very much @3dRaven

https://www.reddit.com/r/unrealengine/comments/7fsw9a/thread_safety_tsharedptr_and_pathfinding/

Something very similar to your current task. A quite in-depth discussion about thread safety and threaded workflow.

And Another tip : anything marked as UPROPERTY is stopped from getting GC’d

1 Like