Description

In URigHiearchyController::AddElement (and other functions), arbitrary delegate code is called while holding Hierarchy->ElementsLock, through the Notify function.

URigHierarchy::AddReferencedObjects takes this same lock during garbage collection.

Calling arbitrary code while holding a lock makes the code vulnerable to a deadlock, and in this case, because of the need for the lock during garbage collection, the deadlock is caused by the delegate itself.

URighHiearchy.ModifiedEvent is a delegate called by notify, and there exist some UObjects that subscribe to it via AddUObject, such as in ControlRig.cpp.

AddUObject uses a TWeakObjectPtr, and calls Internal_Pin on it during the delegate’s broadcast, which includes an FGCScopeGuard.

So on the task thread, we enter the locks in the order

  • ElementsLock
  • FGCScopeGuard

And on the GameThread running garbage collection, we enter them in the opposite order

  • FGCScopeGuard
  • ElementsLock

Which causes a deadlock.

The likely solution is to call Notify outside the lock.

But we call Notify inside the lock because it passes pointers to private data that might be deleted by other functions called on the URigHiearchy.

To call Notify outside the lock, we need either to pass a copy of the data instead of a pointer, or we need a separate locking structure that adds a readlock that prevents those pointers from being modified or deleted while the Notify is being called inside the readlock but outside of the ElementsLock.

Callstacks of the deadlock included in comments.

Steps to Reproduce

It should be possible to create a synthetic repro with custom c++ instrumentation:

  • Cook a package with a URigHiearchyController.
  • Cause URigHierarchyController::AddElement to be called on a task thread.
  • Add code in AddElement when called on a TaskThread to request a global flag readable by the cooker to request a garbage collect.
  • Ad code in UCookOnTheFlyCommandlet::TickMainCookLoop to set `StackData.ResultFlags |= COSR_RequiresGC | COSR_YieldTick` when that flag is true.
  • The garbage collect should deadlock because it can not get access to the RigHierarchyController while AddElement is holding the lock.

Have Comments or More Details?

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

0
Login to Vote

Unresolved
CreatedApr 15, 2026
UpdatedMay 11, 2026
View Jira Issue