Description

A user has reported a failing ensure in FMRSWRecursiveAccessDetector::AcquireWriteAccess(), a race condition that has a small chance of happening when launching the editor.

FLiveCodingModule::StartLiveCoding() accesses FModuleManager::Get().OnModulesChanged() from an arbitrary thread, without any thread safety mechanism. Meanwhile, that same delegate can be accessed by any other thread, including the GameThread when it loads modules.

See the two attached callstacks. Be aware that the callstacks were deduced from the user story rather than user provided, but code inspection of FLiveCodingModule::StartLiveCoding() should reveal that its access of FModuleManager::Get().OnModulesChanged() is unsafe.

Steps to Reproduce

.

Callstack

Callstack 1:
> UnrealEditor-LiveCoding.dll!FLiveCodingModule::StartLiveCoding(ELiveCodingStartupMode StartupMode) Line 1124 C++
  [Inline Frame] UnrealEditor-LiveCoding.dll!UE::Core::Private::Function::TFunctionRefBase<UE::Core::Private::Function::TFunctionStorage<1>,void __cdecl(void)>::operator()() Line 629 C++
  [Inline Frame] UnrealEditor-LiveCoding.dll!TFunctionGraphTaskImpl<void __cdecl(void),0>::DoTaskImpl(TUniqueFunction<void __cdecl(void)> & Function, ENamedThreads::Type) Line 1754 C++
  [Inline Frame] UnrealEditor-LiveCoding.dll!TFunctionGraphTaskImpl<void __cdecl(void),0>::DoTask(ENamedThreads::Type) Line 1747 C++
  UnrealEditor-LiveCoding.dll!TGraphTask<TFunctionGraphTaskImpl<void __cdecl(void),0>>::ExecuteTask(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & NewTasks, ENamedThreads::Type CurrentThread, bool bDeleteOnCompletion) Line 1265 C++
  [Inline Frame] UnrealEditor-Core.dll!FBaseGraphTask::Execute(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> &) Line 866 C++
  [Inline Frame] UnrealEditor-Core.dll!FTaskGraphCompatibilityImplementation::QueueTask::__l5::<lambda>() Line 1973 C++
  UnrealEditor-Core.dll!LowLevelTasks::FTask::Init::__l11::<lambda>(const bool bNotCanceled) Line 499 C++
  [Inline Frame] UnrealEditor-Core.dll!Invoke(LowLevelTasks::FTask::Init::__l11::void <lambda>(const TArray<FString,TSizedDefaultAllocator<32>> &, UWorld *, FOutputDevice &) &) Line 47 C++
  [Inline Frame] UnrealEditor-Core.dll!LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`FTaskGraphCompatibilityImplementation::QueueTask'::`5'::void <lambda>(const TArray<FString,TSizedDefaultAllocator<32>> &, UWorld *, FOutputDevice &)>'::`11'::void <lambda>(const TArray<FString,TSizedDefaultAllocator<32>> &, UWorld *, FOutputDevice &),0>::Call(void *) Line 162 C++
  UnrealEditor-Core.dll!LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`FTaskGraphCompatibilityImplementation::QueueTask'::`5'::void <lambda>(const TArray<FString,TSizedDefaultAllocator<32>> &, UWorld *, FOutputDevice &)>'::`11'::void <lambda>(const TArray<FString,TSizedDefaultAllocator<32>> &, UWorld *, FOutputDevice &),0>::CallAndMove(LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48> & Destination, void * InlineData, unsigned int DestInlineSize, bool <Params_0>) Line 171 C++
  [Inline Frame] UnrealEditor-Core.dll!LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::CallAndMove(LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48> &) Line 308 C++
  UnrealEditor-Core.dll!LowLevelTasks::FTask::ExecuteTask() Line 627 C++
  UnrealEditor-Core.dll!LowLevelTasks::FScheduler::ExecuteTask(LowLevelTasks::FTask * & InOutTask) Line 150 C++
  UnrealEditor-Core.dll!LowLevelTasks::FScheduler::TryExecuteTaskFrom<LowLevelTasks::TLocalQueueRegistry<1024>::TLocalQueue,&LowLevelTasks::TLocalQueueRegistry<1024>::TLocalQueue::DequeueGlobal,0>(LowLevelTasks::TLocalQueueRegistry<1024>::TLocalQueue * Queue, LowLevelTasks::TLocalQueueRegistry<1024>::FOutOfWork & OutOfWork, bool bPermitBackgroundWork, bool bDisableThrottleStealing) Line 350 C++
  UnrealEditor-Core.dll!LowLevelTasks::FScheduler::WorkerMain(LowLevelTasks::FSleepEvent * WorkerEvent, LowLevelTasks::TLocalQueueRegistry<1024>::TLocalQueue * WorkerLocalQueue, unsigned int WaitCycles, bool bPermitBackgroundWork) Line 378 C++
  [Inline Frame] UnrealEditor-Core.dll!LowLevelTasks::FScheduler::CreateWorker::__l2::<lambda>() Line 70 C++
  [Inline Frame] UnrealEditor-Core.dll!Invoke(LowLevelTasks::FScheduler::CreateWorker::__l2::void <lambda>(const TArray<FString,TSizedDefaultAllocator<32>> &, UWorld *, FOutputDevice &) &) Line 47 C++
  UnrealEditor-Core.dll!UE::Core::Private::Function::TFunctionRefCaller<`LowLevelTasks::FScheduler::CreateWorker'::`2'::void <lambda>(const TArray<FString,TSizedDefaultAllocator<32>> &, UWorld *, FOutputDevice &),void __cdecl(void)>::Call(void * Obj) Line 480 C++
  [Inline Frame] UnrealEditor-Core.dll!UE::Core::Private::Function::TFunctionRefBase<UE::Core::Private::Function::TFunctionStorage<1>,void __cdecl(void)>::operator()() Line 629 C++
  UnrealEditor-Core.dll!FThreadImpl::Run() Line 69 C++
  UnrealEditor-Core.dll!FRunnableThreadWin::Run() Line 149 C++
  UnrealEditor-Core.dll!FRunnableThreadWin::GuardedRun() Line 71 C++
 [External Code]
 
Callstack 2:
> UnrealEditor-Core.dll!FModuleManager::AddModuleToModulesList(const FName InModuleName, TSharedRef<FModuleManager::FModuleInfo,1> & InModuleInfo) Line 292 C++
  UnrealEditor-Core.dll!FModuleManager::AddModule(const FName InModuleName) Line 315 C++
  UnrealEditor-Core.dll!FModuleManager::LoadModuleWithFailureReason(const FName InModuleName, EModuleLoadResult & OutFailureReason, ELoadModuleFlags InLoadModuleFlags) Line 473 C++
  UnrealEditor-Projects.dll!FModuleDescriptor::LoadModulesForPhase(ELoadingPhase::Type LoadingPhase, const TArray<FModuleDescriptor,TSizedDefaultAllocator<32>> & Modules, TMap<FName,enum EModuleLoadResult,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<FName,enum EModuleLoadResult,0>> & ModuleLoadErrors) Line 696 C++
  UnrealEditor-Projects.dll!FPluginManager::TryLoadModulesForPlugin(const FPlugin & Plugin, const ELoadingPhase::Type LoadingPhase) Line 2404 C++
  UnrealEditor-Projects.dll!FPluginManager::LoadModulesForEnabledPlugins(const ELoadingPhase::Type LoadingPhase) Line 2477 C++
  UnrealEditor.exe!FEngineLoop::LoadStartupModules() Line 4664 C++
  UnrealEditor.exe!FEngineLoop::PreInitPostStartupScreen(const wchar_t * CmdLine) Line 3942 C++
  [Inline Frame] UnrealEditor.exe!FEngineLoop::PreInit(const wchar_t *) Line 4414 C++
  [Inline Frame] UnrealEditor.exe!EnginePreInit(const wchar_t *) Line 41 C++
  UnrealEditor.exe!GuardedMain(const wchar_t * CmdLine) Line 140 C++
  UnrealEditor.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) Line 247 C++
  UnrealEditor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) Line 298 C++
 [External Code]
 

Have Comments or More Details?

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

0
Login to Vote

Fixed
ComponentUE - Foundation - Cpp Tools - Live Coding
Affects Versions5.3
Target Fix5.4
Fix Commit26125990
CreatedDec 19, 2023
ResolvedDec 19, 2023
UpdatedJan 11, 2024