Smart Objects Crash fix

Hi, I wanted to call attention to an engine crash fix with an open PR here:

https://github.com/EpicGames/UnrealEngine/pull/13135

Reposting the summary:

Fixes a crash in the smart object system related to UAITask_UseGameplayBehaviorSmartObject not cleaning up it’s MoveTo properly. If the task is canceled, it doesn’t clean up it’s MoveTo, which gets destroyed eventually as part of the smart object system cleanup, but if the parent level has already been marked for garbage, UWorld* is null causing a crash.

Of note, UAITask_UseGameplayBehaviorSmartObject also doesn’t seem to properly handle cleaning up it’s internal MoveTo task if ExternalCancel() is called, but we’ve left that for another time.

Hopefully we can get this upstreamed or at least get a more official fix, thanks.

I can see the PR has been migrated into our JIRA instance and is awaiting being assigned. I know there has been some work on cleaning up various tasks that may be leftover. Is the callstack you provided for the ExternalCancel issue with the MoveTo task? Do you have the callstack for the crash relating to your PR as well?

-James

Thank you! We are working through PRs currently and this one has been assigned to a dev. We are hoping to include it in 5.6. We did recently add another fix for the SmartObject AITask where a null pawn during shutdown could trigger a check. It should be in 5.6 as well, but I do not think it is in the preview release or UE5 Main stream just yet.

Hi James,

Sorry for not providing this earlier. The callstack of the crash we were seeing is below - our PR resolves this issue. In this case the this pointer was NULL (an invalid UWorld pointer).

> UnrealEditor-Engine.dll!UWorld::GetTimerManager() Line 7070 C++ UnrealEditor-AIModule.dll!UAITask_MoveTo::ResetTimers() Line 252 C++ UnrealEditor-AIModule.dll!UAITask_MoveTo::OnDestroy(bool bInOwnerFinished) Line 265 C++ UnrealEditor-GameplayBehaviorSmartObjectsModule.dll!UAITask_UseGameplayBehaviorSmartObject::Abort() Line 306 C++ [Inline Frame] UnrealEditor-GameplayBehaviorSmartObjectsModule.dll!Invoke(void(UAITask_UseGameplayBehaviorSmartObject::*)(const FSmartObjectClaimHandle &, ESmartObjectSlotState)) Line 66 C++ [Inline Frame] UnrealEditor-GameplayBehaviorSmartObjectsModule.dll!UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int>>::ApplyAfter(void(UAITask_UseGameplayBehaviorSmartObject::*)(const FSmartObjectClaimHandle &, ESmartObjectSlotState) &) Line 317 C++ UnrealEditor-GameplayBehaviorSmartObjectsModule.dll!TBaseUObjectMethodDelegateInstance<0,UAITask_UseGameplayBehaviorSmartObject,void __cdecl(FSmartObjectClaimHandle const &,enum ESmartObjectSlotState),FDefaultDelegateUserPolicy>::ExecuteIfSafe(const FSmartObjectClaimHandle & <Params_0>, ESmartObjectSlotState <Params_1>) Line 667 C++ [Inline Frame] UnrealEditor-SmartObjectsModule.dll!TDelegate<void __cdecl(FSmartObjectClaimHandle const &,enum ESmartObjectSlotState),FDefaultDelegateUserPolicy>::ExecuteIfBound(const FSmartObjectClaimHandle & <Params_1>, ESmartObjectSlotState) Line 634 C++ UnrealEditor-SmartObjectsModule.dll!FSmartObjectRuntimeSlot::Release(const FSmartObjectClaimHandle & ClaimHandle, const bool bAborted) Line 169 C++ UnrealEditor-SmartObjectsModule.dll!USmartObjectSubsystem::AbortAll(const FSmartObjectHandle Handle, FSmartObjectRuntime & SmartObjectRuntime) Line 640 C++ UnrealEditor-SmartObjectsModule.dll!USmartObjectSubsystem::DestroyRuntimeInstanceInternal(const FSmartObjectHandle Handle, FSmartObjectRuntime & SmartObjectRuntime) Line 558 C++ UnrealEditor-SmartObjectsModule.dll!USmartObjectSubsystem::RemoveRuntimeInstanceFromSimulation(FSmartObjectRuntime & SmartObjectRuntime, USmartObjectComponent * SmartObjectComponent) Line 544 C++ UnrealEditor-SmartObjectsModule.dll!USmartObjectSubsystem::RemoveComponentFromSimulation(USmartObjectComponent & SmartObjectComponent) Line 603 C++ UnrealEditor-SmartObjectsModule.dll!USmartObjectSubsystem::UnregisterSmartObjectInternal(USmartObjectComponent & SmartObjectComponent, const bool bDestroyRuntimeState) Line 857 C++ UnrealEditor-SmartObjectsModule.dll!USmartObjectSubsystem::UnregisterSmartObject(USmartObjectComponent & SmartObjectComponent) Line 831 C++ UnrealEditor-SmartObjectsModule.dll!USmartObjectComponent::UnregisterFromSubsystem(const ESmartObjectUnregistrationType UnregistrationType) Line 228 C++ UnrealEditor-SmartObjectsModule.dll!USmartObjectComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) Line 267 C++ UnrealEditor-Engine.dll!AActor::EndPlay(const EEndPlayReason::Type EndPlayReason) Line 2849 C++ UnrealEditor-Engine.dll!AActor::RouteEndPlay(const EEndPlayReason::Type EndPlayReason) Line 2796 C++ UnrealEditor-Engine.dll!UWorld::RemoveFromWorld(ULevel * Level, bool bAllowIncrementalRemoval, FNetLevelVisibilityTransactionId TransactionId, ULevelStreaming * InOwningLevelStreaming) Line 3659 C++ UnrealEditor-Engine.dll!ULevelStreaming::UpdateStreamingState(bool & bOutUpdateAgain, bool & bOutRedetermineTarget) Line 1095 C++ [Inline Frame] UnrealEditor-Engine.dll!FStreamingLevelPrivateAccessor::UpdateStreamingState(ULevelStreaming *) Line 791 C++ UnrealEditor-Engine.dll!UWorld::UpdateLevelStreaming() Line 4425 C++ UnrealEditor-Engine.dll!UWorld::FlushLevelStreaming(EFlushLevelStreamingType FlushType) Line 4690 C++ UnrealEditor-UnrealEd.dll!UEditorEngine::TeardownPlaySession(FWorldContext & PieWorldContext) Line 814 C++ UnrealEditor-UnrealEd.dll!UEditorEngine::EndPlayMap() Line 344 C++ UnrealEditor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 2473 C++ UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 550 C++ UnrealEditor-LyraEditor-Win64-DebugGame.dll!ULyraEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 39 C++ UnrealEditor-Win64-DebugGame.exe!FEngineLoop::Tick() Line 5877 C++ [Inline Frame] UnrealEditor-Win64-DebugGame.exe!EngineTick() Line 69 C++ UnrealEditor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine) Line 188 C++ UnrealEditor-Win64-DebugGame.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) Line 266 C++ UnrealEditor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) Line 317 C++ [Inline Frame] UnrealEditor-Win64-DebugGame.exe!invoke_main() Line 102 C++ UnrealEditor-Win64-DebugGame.exe!__scrt_common_main_seh() Line 288 C++ kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown

I don’t really understand the question around ExternalCancel - we weren’t seeing a crash there, I just wanted to mention that it doesn’t seem to properly clean up it’s child MoveTo task, so it seems like it could potentially leak until the world is destroyed.

Thanks,

Reuben

Awesome, thanks James! :slight_smile: