Hey, hello everyone!.
We are using Mutable to create weapons that can contain a variety of attachments. The idea is to build them at runtime based on the different attachments.
In the player base we have a kind of workbench where you can preview your weapons (the 3D model). When the player selects a weapon, we generate the mesh and display it on top of the table. When the player selects another weapon, we destroy the previous
UCustomizableObjectInstance
with
MarkAsGarbage
and then create a new one for the new weapon.The problem is that if you switch weapons quickly, the previous instance may be marked as garbage before the
UpdateMesh
task finishes, which leads to a crash due to a weak pointer in the provided callstack.On the other hand, if I don’t mark it as garbage and wait for all references to finish — even if I force garbage collection every frame — everything completes without crashing. This makes me think that somewhere inside Mutable, the system is still holding a reference to that
UCustomizableObjectInstance
, preventing deletion and avoiding the crash.
CleanupCustomizableObjectInstance()
{
UCustomizableObjectInstance* CustomizableObjectInstance = GetCustomizableObjectInstance();
if (IsValid(CustomizableObjectInstance))
{
CustomizableObjectInstance->UpdatedNativeDelegate.Clear();
CustomizableObjectInstance->MarkAsGarbage();
}
SetCustomizableObjectInstance(nullptr);
}
Question:
What is the proper way of cleaning up a
UCustomizableObjectInstance
safely, without running into these issues?
[Inlined] IsObjectHandleTypeSafe(FObjectHandlePrivate) ObjectHandle.h:187
[Inlined] ObjectPtr_Private::Get(const FObjectPtr &) ObjectPtr.h:469
[Inlined] TObjectPtr::Get() ObjectPtr.h:681
[Inlined] TObjectPtr::operator class UCustomizableObject *() ObjectPtr.h:708
UCustomizableObjectInstance::GetCustomizableObject() CustomizableObjectInstance.cpp:682
impl::Task_Mutable_GetMeshes_GetImages(const TSharedRef<…> &, double) CustomizableObjectSystem.cpp:2537
impl::Task_Mutable_GetMeshes_GetMesh_Loop(const TSharedRef<…> &, double, const TSharedRef<…> &, int) CustomizableObjectSystem.cpp:2770
impl::Task_MutableGetMeshes_GetMesh_Post(const TSharedRef<…> &, double, const TSharedRef<…> &, int, TTask<…>) CustomizableObjectSystem.cpp:2755
[Inlined] impl::Task_Mutable_GetMeshes_GetMesh_Loop::__l2::<lambda_1>::operator()() CustomizableObjectSystem.cpp:2780
[Inlined] Invoke(<lambda_1> &) Invoke.h:47
UE::Tasks::Private::TExecutableTaskBase<`impl::Task_Mutable_GetMeshes_GetMesh_Loop'::`2'::<lambda_1>,void,void>::ExecuteTask() TaskPrivate.h:904
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:387
[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