Description

User reported game thread and async loading thread accessing a UGameplayEffect property at the same time:

  • Game thread having loaded a GameplayEffectA and in its PostLoad() updating the property InheritableGameplayEffectTags.CombinedTags
  • Async loading thread currently loading a GameplayEffectB which inherits from GameplayEffectA, it can write to the same InheritableGameplayEffectTags.CombinedTags according to the user which they verified by memory address.

The two threads are accessing the same memory, which we should have a mechanism to prevent. One thing to investigate whether that should be specific to the GameplayEffect FInheritedTagContainer properties or higher up: a UObject CDO's PostLoad and a UObject's CDO being invoked by a subclass on the loading thread.

Steps to Reproduce
  • Open Lyra editor.
  • Add GE_ClassA blueprint class with GameplayEffect as base
  • Add GE_ClassB with GE_ClassA as base
  • Add GE_ClassC with GE_ClassB as base (unsure if GE_ClassC is necessary, but I did it)
  • Add Ability.ActivateFail.ActivationGroup to GE_ClassA -> Tags -> GameplayEffectAssetTag -> Added
  • Add Ability.ActivateFail.CellAlreadyContainsBomb to GE_ClassB -> Tags -> GameplayEffectAssetTag -> Added (clear inherited tags first)
  • Add Ability.ActivateFail.Cooldown to GE_ClassC -> Tags -> GameplayEffectAssetTag -> Added (clear inherited tags first)
  • Modify the asset Phase_PostGame -> Gameplay Ability Graph, found in the ShooterCore plugin
  • Copy/paste the Apply Effect to All node, set Effect to GE_ClassC, and wire it up
  • Save all and close editor
  • Make staged build, Win64 Development is fine (RunUAT.bat BuildCookRun -project=Samples/Games/Lyra/Lyra.uproject -Target=LyraGame -configuration=Development -build -cook -pak -stage -platform=Win64)
  • LyraGame.exe -waitforattach, and connect Visual Studio debugger
  • Click Play Lyra
  • In Visual Studio, in GameplayEffect.cpp, set breakpoint on UpdateInheritedTagProperties() line of UGameplayEffect::PostLoad()
  • Set breakpoint at the top of UGameplayEffect constructor.
  • Select Quickplay
  • Wait until UGameplayEffect::PostLoad() shows GE_ClassA, and freeze game thread (be aware breakpoint below might hit first)
  • Wait until UGameplayEffect::UGameplayEffect shows GE_ClassB, and freeze loading thread (be aware breakpoint above might hit first)
  • Thaw game thread, and keep pressing F11 until you are on the first Reset() line of FInheritedTagContainer::UpdateInheritedTagProperties before freezing again
  • Note address of &CombinedTags in Watch window (e.g. 0x000001515385e7b8)
  • Set conditional breakpoint at the top of FGameplayTagContainer::operator= (e.g. &Other == 0x000001515385e7b8)
  • Thaw the loading thread and continue execution (F5) to hit conditional breakpoint
  • Tada! The game and loading threads are fighting over the same data.

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-166872 in the post.

0
Login to Vote

Fixed
ComponentUE - Gameplay - Gameplay Ability System
Affects Versions5.05.1
Target Fix5.3
Fix Commit24279266
Main Commit24279266
CreatedOct 12, 2022
ResolvedFeb 20, 2023
UpdatedSep 30, 2024
View Jira Issue