Network Prediction Components NetworkPredictionProxy ReadAuxState and ReadSyncState functions are not thread safe

Hello,

I’ve ran into an intermittent crash when using multithreaded animation blueprints that read directly from the Network Prediction Component’s NetworkPredictionProxy while using the PresentationState. Switching over to using the Simulation state fixes this issue.

The DataArray variable shows as invalid during the crash so I’m assuming that it is triggering on an uninitialized array? Data from the iterator is null in this case.

Steps to Reproduce
This repro is theoretical but very similar to what I’ve done in my own project.

  • Open the Lyra project.
  • Change the multithreaded getters to pull the information directly from the simulation via NetworkPredictionProxy.ReadSyncState<FGSShootingSyncState>(ENetworkPredictionStateRead::Presentation).
  • Run the game in PIE with 5+ characters in client mode

Hi,

Thank you for the report. In order to better assist you, would you be able to provide a basic sample project reproducing the issue?

Thanks,

Alex

Hi Kieran - by any chance, does this crash only happen once you’re over a certain number of clients?

We have this change that addresses Presentation-related crashes that would occur as more simulated objects are added.

It hasn’t been in any released UE versions yet, but you can see it here:

https://github.com/EpicGames/UnrealEngine/commit/50b5a5076a6408d4bec214d76a423890b17760ac

Before I spin this up, can you share the callstack with the crash occurring? And at the time of the crash, are there any other callstacks from other threads that look like it might be concurrently accessing the presentation view?

Also helpful: anything in the logs leading up to the crash

Nothing obvious from the call stack. I’ll see if we can reproduce it here with the project and plugin.

I’m not having any luck reproducing the crash here.

I started with a clean UE 5.6.1 installation, using the project and plugin you linked, and made the changes to UGSShootingComponent.

Experimenting with Network Prediction settings, increased player counts, network emulation settings, etc. didn’t make a difference.

Could there be some additional project setup required?

Ok it sounds like frame rate could be a deciding factor. I’ll experiment with that and report back.

Stay tuned!

Hi Alex,

I’m afraid I don’t really have time to put the sample together in the near future. As mentioned, I’ve switched over to using the Simulation state which has unblocked me for now.

Sorry!

Hi Justin,

I only notice it when I’m testing with 4 or more clients but I think it’s due to it being more likely to trigger at the right time rather than a predictable crash. I tried implementing this change with no luck unfortunately, it still crashes for me.

Do you have access to Fab plugins? If so I can provide you with a repro:

Get the project from GitHub - KrakenParty/GunsmithExamples: A sample project to accompany the Gunsmith plugin for Unreal Engine

Get the plugin from https://www.fab.com/listings/915f3703\-792a\-4156\-a7a8\-a01ee6bb798e

In UGSShootingComponent::GetSyncState and GetAuxState change the ReadSyncState calls to use ENetworkPredictionStateRead::Presentation

Load the L_Multiplayer map with 4 clients. Late joining a 5th client sometimes triggers but if not, start shooting randomly and you should hit it within a few seconds.

Ah sorry, I thought I had already provided the callstack. Here’s the most frequent callstack but it can happen anywhere that I use the states that I retrieved via ReadSyncState/ReadAuxState.

<unknown> 0x0000000200000002
FMoverDataCollection::FindDataByType(const UScriptStruct *) MoverTypes.cpp:387
UCustomComponent::GetIsShooting() GSShootingComponent.cpp:588
UFunction::Invoke(UObject *, FFrame &, void *const) Class.cpp:7453
FPropertyAccessSystem::CallNativeAccessor(UObject *, UFunction *, void *) PropertyAccess.cpp:442
FPropertyAccessSystem::GetAccessAddress<`FPropertyAccessSystem::ProcessCopy'::`11'::<lambda_1> >(void *,const FPropertyAccessLibrary &,const FPropertyAccessIndirectionChain &,<lambda_1>) PropertyAccess.cpp:536
FPropertyAccessSystem::ProcessCopy(UStruct *, void *, const FPropertyAccessLibrary &, int, int, TFunctionRef<…>) PropertyAccess.cpp:618
[Inlined] FPropertyAccessSystem::ProcessCopies(UStruct *, void *, const FPropertyAccessLibrary &, int) PropertyAccess.cpp:648
PropertyAccess::ProcessCopies(UObject *, const FPropertyAccessLibrary &, const PropertyAccess::FCopyBatchId &) PropertyAccess.cpp:742
FAnimSubsystem_PropertyAccess::OnPreUpdate_GameThread(FAnimSubsystemUpdateContext &) AnimSubsystem_PropertyAccess.cpp:12
[Inlined] UAnimInstance::UpdateAnimation::__l40::<lambda_1>::operator()(const FAnimSubsystemInstanceContext &) AnimInstance.cpp:625
[Inlined] Invoke(<lambda_1> &, const FAnimSubsystemInstanceContext &) Invoke.h:47
UE::Core::Private::Function::TFunctionRefCaller<`UAnimInstance::UpdateAnimation'::`40'::<lambda_1>,enum EAnimSubsystemEnumeration,FAnimSubsystemInstanceContext const &>::Call(void *,const FAnimSubsystemInstanceContext &) Function.h:320
[Inlined] UE::Core::Private::Function::TFunctionRefBase::operator()(const FAnimSubsystemInstanceContext &) Function.h:471
UAnimBlueprintGeneratedClass::ForEachSubsystem(UObject *, TFunctionRef<…>) AnimBlueprintGeneratedClass.cpp:431
UAnimInstance::UpdateAnimation(float, bool, EUpdateAnimationFlag) AnimInstance.cpp:622
USkeletalMeshComponent::TickAnimInstances(float, bool) SkeletalMeshComponent.cpp:1729
USkeletalMeshComponent::TickAnimation(float, bool) SkeletalMeshComponent.cpp:1680
USkeletalMeshComponent::TickPose(float, bool) SkeletalMeshComponent.cpp:1883
USkinnedMeshComponent::TickComponent(float, ELevelTick, FActorComponentTickFunction *) SkinnedMeshComponent.cpp:1667
USkeletalMeshComponent::TickComponent(float, ELevelTick, FActorComponentTickFunction *) SkeletalMeshComponent.cpp:1990
[Inlined] FActorComponentTickFunction::ExecuteTick::__l2::<lambda_1>::operator()(float) ActorComponent.cpp:1591
FActorComponentTickFunction::ExecuteTickHelper<`FActorComponentTickFunction::ExecuteTick'::`2'::<lambda_1> >(UActorComponent *,bool,float,ELevelTick,const <lambda_1> &) Actor.h:4835
FActorComponentTickFunction::ExecuteTick(float, ELevelTick, Type, const TRefCountPtr<…> &) ActorComponent.cpp:1589
[Inlined] FTickFunctionTask::DoTask(Type, const TRefCountPtr<…> &) TickTaskManager.cpp:329
TGraphTask::ExecuteTask() TaskGraphInterfaces.h:706
UE::Tasks::Private::FTaskBase::TryExecuteTask() TaskPrivate.h:527
[Inlined] FBaseGraphTask::Execute(TArray<…> &, Type, bool) TaskGraphInterfaces.h:505
FNamedTaskThread::ProcessTasksNamedThread(int, bool) TaskGraph.cpp:779
FNamedTaskThread::ProcessTasksUntilIdle(int) TaskGraph.cpp:678
[Inlined] FTaskGraphCompatibilityImplementation::ProcessThreadUntilIdle(Type) TaskGraph.cpp:1419
FTaskGraphCompatibilityImplementation::ProcessUntilTasksComplete(const TArray<…> &, Type, const TFunction<…> &) TaskGraph.cpp:1557
FTickTaskSequencer::ReleaseTickGroup(ETickingGroup, bool, TArray<…> &) TickTaskManager.cpp:986
FTickTaskManager::RunTickGroup(ETickingGroup, bool) TickTaskManager.cpp:2078
UWorld::RunTickGroup(ETickingGroup, bool) LevelTick.cpp:783
UWorld::Tick(ELevelTick, float) LevelTick.cpp:1511
UEditorEngine::Tick(float, bool) EditorEngine.cpp:2149
UUnrealEdEngine::Tick(float, bool) UnrealEdEngine.cpp:530
FEngineLoop::Tick() LaunchEngineLoop.cpp:5619
[Inlined] EngineTick() Launch.cpp:60
GuardedMain(const wchar_t *) Launch.cpp:189
LaunchWindowsStartup(HINSTANCE__ *, HINSTANCE__ *, char *, int, const wchar_t *) LaunchWindows.cpp:271
WinMain(HINSTANCE__ *, HINSTANCE__ *, char *, int) LaunchWindows.cpp:339

There is nothing in the logs related to this. I also have 2 foreground workers with potentially relevant callstacks which I can post in the next reply.

[Inlined] TSizedHeapAllocator<32,FMemory>::ForAnyElementType::{dtor}() ContainerAllocationPolicies.h:685
[Inlined] TSet<TTuple<FName,TArray<UE::Anim::FMessageStack::FNodeInfo,TSizedInlineAllocator<4,32,TSizedDefaultAllocator<32> > > >,TDefaultMapHashableKeyFuncs<FName,TArray<UE::Anim::FMessageStack::FNodeInfo,TSizedInlineAllocator<4,32,TSizedDefaultAllocator<32> > >,0>,TInlineSetAllocator<4,TSetAllocator<TSparseArrayAllocator<TSizedDefaultAllocator<32>,TSizedDefaultAllocator<32> >,TSizedDefaultAllocator<32>,2,8,4>,2,4> >::{dtor}() Set.h:199
SharedPointerInternals::TIntrusiveReferenceController::DestroyObject() SharedPointerInternals.h:424
[Inlined] SharedPointerInternals::TReferenceControllerBase::ReleaseSharedReference() SharedPointerInternals.h:227
[Inlined] SharedPointerInternals::FSharedReferencer<1>::{dtor}() SharedPointerInternals.h:606
DestructItems<…>(FAnimNode_SaveCachedPose::FCachedUpdateContext *, int) MemoryOps.h:108
[Inlined] TArray::Reset(int) Array.h:2270
FAnimNode_SaveCachedPose::PostGraphUpdate() AnimNode_SaveCachedPose.cpp:230
FAnimInstanceProxy::UpdateAnimationNode_WithRoot(const FAnimationUpdateContext &, FAnimNode_Base *, FName) AnimInstanceProxy.cpp:152
FAnimInstanceProxy::UpdateAnimationNode(const FAnimationUpdateContext &) AnimInstanceProxy.cpp:123
FAnimInstanceProxy::UpdateAnimation_WithRoot(const FAnimationUpdateContext &, FAnimNode_Base *, FName) AnimInstanceProxy.cpp:1369
FAnimInstanceProxy::UpdateAnimation() AnimInstanceProxy.cpp:1238
UAnimInstance::ParallelUpdateAnimation() AnimInstance.cpp:830
USkeletalMeshComponent::PerformAnimationProcessing(const USkeletalMesh *, UAnimInstance *, bool, bool, TArray<…> &, TArray<…> &, UE::Math::TVector<…> &, FBlendedHeapCurve &, UE::Anim::FMeshAttributeContainer &) SkeletalMeshComponent.cpp:2439
USkeletalMeshComponent::ParallelAnimationEvaluation() SkeletalMeshComponent.cpp:4665
FParallelAnimationEvaluationTask::DoTask(Type, const TRefCountPtr<…> &) SkeletalMeshComponent.cpp:366
TGraphTask::ExecuteTask() TaskGraphInterfaces.h:706
UE::Tasks::Private::FTaskBase::TryExecuteTask() TaskPrivate.h:527
[Inlined] UE::Tasks::Private::FTaskBase::Init::__l2::<lambda_1>::operator()() TaskPrivate.h:184
[Inlined] LowLevelTasks::FTask::Init::__l13::<lambda_1>::operator()(const bool) Task.h:499
[Inlined] Invoke(LowLevelTasks::FTask::<lambda_1> &, bool &) Invoke.h:47
[Inlined] LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2'::<lambda_1> >'::`13'::<lambda_1>,0>::Call(void *,bool) TaskDelegate.h:162
LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2'::<lambda_1> >'::`13'::<lambda_1>,0>::CallAndMove(LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48> &,void *,unsigned int,bool) TaskDelegate.h:171
[Inlined] LowLevelTasks::TTaskDelegate::CallAndMove(LowLevelTasks::TTaskDelegate<…> &, bool) TaskDelegate.h:309
LowLevelTasks::FTask::ExecuteTask() Task.h:627
LowLevelTasks::FScheduler::ExecuteTask(LowLevelTasks::FTask *) Scheduler.cpp:365
[Inlined] LowLevelTasks::FScheduler::TryExecuteTaskFrom(LowLevelTasks::Private::FWaitEvent *, LowLevelTasks::Private::TLocalQueueRegistry<…>::TLocalQueue *, LowLevelTasks::Private::FOutOfWork &, bool) Scheduler.cpp:665
LowLevelTasks::FScheduler::WorkerLoop(LowLevelTasks::Private::FWaitEvent *, LowLevelTasks::Private::TLocalQueueRegistry<…>::TLocalQueue *, unsigned int, bool) Scheduler.cpp:724
[Inlined] LowLevelTasks::FScheduler::WorkerMain(LowLevelTasks::Private::FWaitEvent *, LowLevelTasks::Private::TLocalQueueRegistry<…>::TLocalQueue *, unsigned int, bool) Scheduler.cpp:783
`LowLevelTasks::FScheduler::CreateWorker'::`2'::<lambda_1>::operator()() Scheduler.cpp:188
[Inlined] UE::Core::Private::Function::TFunctionRefBase::operator()() Function.h:471
FThreadImpl::Run() Thread.cpp:66
FRunnableThreadWin::Run() WindowsRunnableThread.cpp:156
FRunnableThreadWin::GuardedRun() WindowsRunnableThread.cpp:71

Hi Justin,

Sorry for the slow response. We’ve been without UDN for a few weeks and have just gained access again.

I don’t believe there is anything else required as when I tested it, it was using the most up to date version of both. I did test it on 5.6.0 but I’ve just managed to repro it again on 5.6.1 with 4 clients, late joining a 5th on L_Multiplayer and shooting often while running and looking around. I’ll test with a completely fresh install on the Epic launcher version of the engine when I get a few minutes to see if that acts differently at all.

I did notice that my game world runs quite slowly with this many clients. Frame time is sitting around 22ms.

I’ve just tested this in a completely fresh build with the same setup as above, 5.6.1 from Epic Launcher and was able to reproduce it pretty quickly. If you’re not able to it could be a hardware issue as it’s fairly consistent for me!