Preventing crashes due to multithreading - HiIAmMoot/RuntimeBPs GitHub Wiki

Preventing crashes due to multithreading

Not all functions can be multithreaded. Spawning an actor or printing a string must happen on the GameThread. Any return values that are from functions executed in the GameThread must be declared variables in the header. In this example one of such variables is SpawnedActor.

// cpp
void USpawn::Execute(int Index, int FromLoopIndex)
{
	SpawnedActor = nullptr;
	UClass* ActorToSpawn = GetConnectedPinValue(InputPins[1]).GetClassArg();

	if (ActorToSpawn)
	{
		FTransform const& Transform = GetConnectedPinValue(InputPins[2]).GetTransformArg();
		FActorSpawnParameters SpawnParameters = FActorSpawnParameters();
		SpawnParameters.Owner = BPConstructor->GetOwner();
		UWorld* World = GetWorld();
		if (BPConstructor->GetMultiThread())
		{
			// We must execute the actual spawning inside the GameThread, a crash will occur otherwise
			AsyncTask(ENamedThreads::GameThread, [this, World, ActorToSpawn, Transform, SpawnParameters, FromLoopIndex]()
			{
				SpawnedActor = World->UWorld::SpawnActorAbsolute(ActorToSpawn, Transform, SpawnParameters);
				OutputPins[1].Value.Array[0].SetActorArg(SpawnedActor);
				URuntimeBpConstructor::Thread->ContinueExecute(BPConstructor, NodeIndex, 0, FromLoopIndex, FunctionIndex);
			});
		}
		else
		{
			SpawnedActor = World->UWorld::SpawnActorAbsolute(ActorToSpawn, Transform, SpawnParameters);
			OutputPins[1].Value.Array[0].SetActorArg(SpawnedActor);
			Super::Execute(0, FromLoopIndex);// Index here is the output pins array index
		}
	}
	else
	{
		Super::Execute(0, FromLoopIndex);// Index here is the output pins array index
	}
}
// .h
UCLASS()
class RUNTIMEBLUEPRINTS_API USpawn : public URuntimeBpObject
{
	GENERATED_BODY()

public:

	// This variable is set once the actor is spawned. Reason why it needs to be a variable here and not a local variable in the function is because of multithreading
	// Spawning can only happen in the gamethread, and if this node is called from an async thread then it needs to make sure the actor is spawned in the gamethread
	// While making sure the actor reference is returned properly so it can be accessed by the async thread.
	AActor* SpawnedActor;

	USpawn();

	virtual void Execute(int Index, int FromLoopIndex = -1) override;
};