Crash as soon as map loads; AsyncTask GetWolrd()?

Hello there,

I have been working on a AI plug in again, and I am now doing the multithreaded cover deciding, but I crash and I don’t know why.

It seems to crash on “if(UWorld* World = This->GetWorld()) {}”, but it is in if statements so as far as I know it should not crash…

This is the code I use to call the ThreadManagerThread::Run (via JoyInit, Init and Constructor of course):

void ACoverAI_BasicGameMode::Update()
{	
//...
//... Stuff that just empties arrays, stops threads if running and resets variables
//...
{	//Extra scope to prevent misinterpretation of variables
		//Restart ThreadManagerThread
		UE_LOG(LogTemp, Warning, TEXT("Restart ThreadManagerThread"));
		ThreadManagerThread* Thread = ThreadManagerThread::JoyInit();
		Thread->This = this;		//An ASomething is needed for GetWorld() for spawning, raytracing etc.
	}

//Other threads come here but are commented out
}

This is the code I try to run:

//the code that actually runs on the thread
uint32 ThreadManagerThread::Run()
{
	FPlatformProcess::Sleep(0.5);
	UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: Run()"));
	//Wait until This is set for proper spawning, take no chances
	while (!This)
	{
		UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: waiting until This is set"));
		FPlatformProcess::Sleep(0.01);
	}
	UE_LOG(LogTemp, Warning, TEXT("This should be set"));
	UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: GetWorld()"));
	if (UWorld* World = This->GetWorld()) //CRASH
	{
		UE_LOG(LogTemp, Warning, TEXT("GetWorld() succeeded"));
		UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: Cast to ACoverAI_BasicGameMode"));
		if (ACoverAI_BasicGameMode* GameMode = Cast<ACoverAI_BasicGameMode>(UGameplayStatics::GetGameMode(This)))
		{
			UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: Cast to ACoverAI_BasicGameMode succeeded"));
			//Never stop this thread, unless its destructor is called
			while (true)
			{
				UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: Entered infinte loop"));
				//Calculate all Enemy threads (Dangerous for enemies, so the characters of the thread are friendly)
				{	//Extra scope to prevent variables from being misinterpreted
					FVector Location;
					//Needed for spawning of the thread, we will only change its location
					FTransform Transform;
					//Set new array with all friendly characters, this array will hold actors which are not in a thread yet
					TArray<AActor*> FriendliesToUse;
					FriendliesToUse.Append(GameMode->Friendlies);
					for (int i = 0; i < FriendliesToUse.Num(); i++)
					{
						TArray<ABasicCharacter*> CharactersForThread;
						if (FriendliesToUse[i])
						{
							//Set spawn and comparison location to the current tester character's location
							Location = FriendliesToUse[i]->GetActorLocation();
							for (int j = 0; j < FriendliesToUse.Num(); j++)
							{
								if (FriendliesToUse[j])
								{
									//Is the distance smaller or equal to the maximum allowed distance, add to the new thread of so
									if ((Location - FriendliesToUse[j]->GetActorLocation()).Size() <= GameMode->MaxDistance)
									{		
										//Make sure cast succeeded
										if (ABasicCharacter* ToAdd = Cast<ABasicCharacter>(FriendliesToUse[j]))
										{
											//if this character was in another thread, destroy that thread and remove it from the array of threads
											for (int n = 0; n < GameMode->EnemyThreads.Num(); n++)
											{
												for (int u = 0; u < GameMode->EnemyThreads[n]->Characters.Num(); u++)
												{
													//Check if still valid
													if (GameMode->EnemyThreads[n])
													{
														//Check if still valid
														if (GameMode->EnemyThreads[n]->Characters[u])
														{
															//Has it this character in its characters array?
															if (GameMode->EnemyThreads[n]->Characters[u] == ToAdd)
															{
																//If so destroy that thread and remove it from the array in the game mode
																ACoverAI_ThreadEnemy* Temp = GameMode->EnemyThreads[n];
																GameMode->EnemyThreads.Remove(Temp);
																Temp->Destroy();
															}
														}
													}
													UE_LOG(LogTemp, Warning, TEXT("Sleep(0.01)"));
													//Save resources
													FPlatformProcess::Sleep(0.01);
												}
												UE_LOG(LogTemp, Warning, TEXT("Sleep(0.01)"));
												//Save resources
												FPlatformProcess::Sleep(0.01);
											}
											//Add the character to the array of what actors will be in the thread, and remove it from the list of actors that does not have a thread yet
											CharactersForThread.Add(ToAdd);
											FriendliesToUse.Remove(FriendliesToUse[j]);
										}
									}
								}
								UE_LOG(LogTemp, Warning, TEXT("Sleep(0.01)"));
								//Save resources
								FPlatformProcess::Sleep(0.01);
							}
						}
						//Set last few spawn parameters
						Transform.SetTranslation(Location);
						FActorSpawnParameters SpawnParams;
						SpawnParams.bNoFail = true;

						//Spawning of actors can only be done on the game thread, request game thread to spawn ThreadActor
						AsyncTask(ENamedThreads::GameThread, [=]()
						{
							UE_LOG(LogTemp, Warning, TEXT("Spawining ACoverAI_ThreadEnemy on gamethread: 336"));
							//Spawn Actor and make sure it is spawned correctly
							if (AActor* ThreadActor = World->SpawnActorAbsolute(ACoverAI_ThreadEnemy::StaticClass(), Transform, SpawnParams))
							{
								ACoverAI_ThreadEnemy* ThreadEnemyActor = Cast<ACoverAI_ThreadEnemy>(ThreadActor);
								//Make sure cast succeeded
								if (ThreadEnemyActor)
								{
									//In the spawned thread, set its array of characters
									ThreadEnemyActor->Characters.Append(CharactersForThread);
								}
							}
							else
							{
								UE_LOG(LogTemp, Error, TEXT("Failed to spawn ThreadActor!"));
							}
						});
						//Save resources
						FPlatformProcess::Sleep(0.05);
					}
				}
				//Calculate all Friendly threads (Dangerous for enemies, so the characters of the thread are Enemy)
				{	//Extra scope to prevent variables from being misinterpreted
					FVector Location;
					//Needed for spawning of the thread, we will only change its location
					FTransform Transform;
					//Set new array with all Enemy characters, this array will hold actors which are not in a thread yet
					TArray<AActor*> EnemiesToUse;
					EnemiesToUse.Append(GameMode->Enemies);
					for (int i = 0; i < EnemiesToUse.Num(); i++)
					{
						TArray<ABasicCharacter*> CharactersForThread;
						if (EnemiesToUse[i])
						{
							//Set spawn and comparison location to the current tester character's location
							Location = EnemiesToUse[i]->GetActorLocation();
							for (int j = 0; j < EnemiesToUse.Num(); j++)
							{
								if (EnemiesToUse[j])
								{
									//Is the distance smaller or equal to the maximum allowed distance, add to the new thread of so
									if ((Location - EnemiesToUse[j]->GetActorLocation()).Size() <= GameMode->MaxDistance)
									{
										ABasicCharacter* ToAdd = Cast<ABasicCharacter>(EnemiesToUse[j]);
										//Make sure cast succeeded
										if (ToAdd)
										{
											//if this character was in another thread, destroy that thread and remove it from the array of threads
											for (int n = 0; n < GameMode->FriendlyThreads.Num(); n++)
											{
												for (int u = 0; u < GameMode->FriendlyThreads[n]->Characters.Num(); u++)
												{
													//Check if still valid
													if (GameMode->FriendlyThreads[n])
													{
														//Check if still valid
														if (GameMode->FriendlyThreads[n]->Characters[u])
														{
															//Has it this character in it's characters array?
															if (GameMode->FriendlyThreads[n]->Characters[u] == ToAdd)
															{
																//If so destroy that thread and remove it from the array in the gamemode
																ACoverAI_ThreadFriendly* Temp = GameMode->FriendlyThreads[n];
																GameMode->FriendlyThreads.Remove(Temp);
																Temp->Destroy();
															}
														}
													}
													//Save resources
													FPlatformProcess::Sleep(0.01);
												}
												//Save resources
												FPlatformProcess::Sleep(0.01);
											}
											//Add the character to the array of what actors will be in the thread, and remove it from the list of actors that does not have a thread yet
											CharactersForThread.Add(ToAdd);
											EnemiesToUse.Remove(EnemiesToUse[j]);
										}
									}
								}
								//Save resources
								FPlatformProcess::Sleep(0.01);
							}
						}
						//Set last few spawn parameters
						Transform.SetTranslation(Location);
						FActorSpawnParameters SpawnParams;
						SpawnParams.bNoFail = true;

						//Spawning of actors can only be done on the game thread, request game thread to spawn ThreadActor
						AsyncTask(ENamedThreads::GameThread, [=]()
						{
							UE_LOG(LogTemp, Warning, TEXT("Spawining ACoverAI_ThreadFriendly on gamethread: 428"));
							AActor* ThreadActor = World->SpawnActorAbsolute(ACoverAI_ThreadFriendly::StaticClass(), Transform, SpawnParams);
							//Make sure the Actor is spawned correctly
							if (ThreadActor)
							{
								ACoverAI_ThreadFriendly* ThreadFriendlyActor = Cast<ACoverAI_ThreadFriendly>(ThreadActor);
								//Make sure cast succeeded
								if (ThreadFriendlyActor)
								{
									//In the spawned thread, set its array of characters
									ThreadFriendlyActor->Characters.Append(CharactersForThread);
								}
							}
							else
							{
								UE_LOG(LogTemp, Error, TEXT("Failed to spawn ThreadActor!"));
							}
						});
						//Save resources
						FPlatformProcess::Sleep(0.05);
					}
				}
				FPlatformProcess::Sleep(0.02);
			}
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("GetWorld() failed"));
		}
	}
	//Needed to not get a compiler error, code never gets to here
	return 0;
}

And this is the output log I get just before my crash:

[2017.04.26-18.38.18:903][642]LogTemp:Warning: Beginplay Game Mode
[2017.04.26-18.38.18:903][642]LogTemp:Warning: Emptying arrays
[2017.04.26-18.38.18:903][642]LogTemp:Warning: Restart ThreadManagerThread
[2017.04.26-18.38.18:903][642]LogTemp:Warning: ThreadMangerThread: JoyInit
[2017.04.26-18.38.18:903][642]LogTemp:Warning: ThreadManagerThread Construction
[2017.04.26-18.38.18:905][642]LogTemp:Warning: ThreadMangerThread: Init
[2017.04.26-18.38.18:905][642]LogTemp:Warning: ThreadManagerThread: Run()
[2017.04.26-18.38.18:905][642]LogTemp:Warning: This should be set
[2017.04.26-18.38.18:905][642]LogTemp:Warning: ThreadManagerThread: GetWorld()
[2017.04.26-18.38.18:925][642]PIE: Info Play in editor start time for /Game/Maps/UEDPIE_0_TEST 0.933

The only thing else I can think of is memory shortage, but that seems unlikely since I have over 1 gb left on my 8gb system when I am running everything.

Thanks in advance.

I also tried turning of the replication by the way

Try passing your pointer directly through the constructor of your ThreadManagerThread class.

I have tried that in another thread class, and that was causing issues, after I did it this way it worked in that thread class. Thanks for your suggestion though.

Right now I tried doing the GetWorld() like this:

	UWorld* World;
	AsyncTask(ENamedThreads::GameThread, [&]()
	{
		World = This->GetWorld();
	});
	FPlatformProcess::Sleep(0.05);
	if (World)
	{

It still crashes, but gives me an error for which I do have debiggung symbols:

UE4Editor_CoverGeneration!UE4Function_Private::TFunctionRefCaller<<lambda_0669c9081f4c46e320c61fb252ddaac4>,void __cdecl(void)>::Call() [c:\program files\epic games\ue_4.15\engine\source\runtime\core\public\templates\function.h:244]
UE4Editor_Core!TGraphTask<FAsyncGraphTask>::ExecuteTask() [d:\build\++ue4+release-4.15+compile\sync\engine\source\runtime\core\public\async\taskgraphinterfaces.h:883]
UE4Editor_Core!FNamedTaskThread::ProcessTasksNamedThread() [d:\build\++ue4+release-4.15+compile\sync\engine\source\runtime\core\private\async\taskgraph.cpp:954]
UE4Editor_Core!FNamedTaskThread::ProcessTasksUntilIdle() [d:\build\++ue4+release-4.15+compile\sync\engine\source\runtime\core\private\async\taskgraph.cpp:716]
UE4Editor_Engine!FFrameEndSync::Sync() [d:\build\++ue4+release-4.15+compile\sync\engine\source\runtime\engine\private\unrealengine.cpp:8430]
UE4Editor!FEngineLoop::Tick() [d:\build\++ue4+release-4.15+compile\sync\engine\source\runtime\launch\private\launchengineloop.cpp:3151]
UE4Editor!GuardedMain() [d:\build\++ue4+release-4.15+compile\sync\engine\source\runtime\launch\private\launch.cpp:166]
UE4Editor!GuardedMainWrapper() [d:\build\++ue4+release-4.15+compile\sync\engine\source\runtime\launch\private\windows\launchwindows.cpp:134]
UE4Editor!WinMain() [d:\build\++ue4+release-4.15+compile\sync\engine\source\runtime\launch\private\windows\launchwindows.cpp:210]
UE4Editor!__scrt_common_main_seh() [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:264]
kernel32
ntdll

The output log still is the same.

I solved it, sort of. By making the code like this:

	UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: GetWorld()"));
	UWorld* World;
	AsyncTask(ENamedThreads::GameThread, [&]()
	{
		World = This->GetWorld();
	});
	unsigned char ItterationCount = 0;
	while (World == nullptr)
	{
		FPlatformProcess::Sleep(0.01);
		ItterationCount++;
		if (ItterationCount > 120)
		{
			UE_LOG(LogTemp, Error, TEXT("Wolrd was not fetched correctly from gamethread; shutting down"));
			return 0;
		}
	}
	if (World)
	{

It does not crash while playing, but it freezes after playing…

Perhaps I am shutting it down incorrectly?

Here is what else I have seen to shutdown a thread:

//Stop the thread
void ThreadManagerThread::Shutdown()
{
	if (Runnable)
	{
		delete Runnable;
		Runnable = NULL;
	}
	return;
}


ThreadManagerThread::~ThreadManagerThread()
{
	UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread Destructor"));
	delete Thread;
	Thread = NULL;
}

Thanks in advance

Last thing I tried before I have to go and still freezes after playing:

	UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: GetWorld()"));
	UWorld* World;
	AsyncTask(ENamedThreads::GameThread, [&]()
	{
		World = This->GetWorld();
	});
	unsigned char ItterationCount = 0;
	while (World == nullptr)
	{
		FPlatformProcess::Sleep(0.01);
		ItterationCount++;
		if (ItterationCount > 120)
		{
			UE_LOG(LogTemp, Error, TEXT("Wolrd was not fetched correctly from gamethread; shutting down"));
			AsyncTask(ENamedThreads::GameThread, [&]()
			{
				Runnable->Shutdown();
				Runnable->~ThreadManagerThread();
			});
			return 0;
		}
	}
	if (World)
	{

With last bit of log:

[2017.04.27-08.52.56:030][771]LogTemp:Warning: ThreadManagerThread: Entered infinte loop
[2017.04.27-08.52.56:044][771]LogTemp:Warning: ThreadManagerThread: Entered infinte loop
[2017.04.27-08.52.56:044][771]LogTemp:Warning: ACoverAI_BasicGameMode destructor
[2017.04.27-08.52.56:044][771]LogTemp:Warning: ThreadManagerThread Destructor

In which entered infinte loop is the top of the repeated loop on the thread