5.5.4 - SteamController plugin has race condition on shutdown accessing Steam Input dll

Hi, it seems that the SteamController plugin in 5.5.4 has a chance to hit a race condition on application shutdown where the FSteamController destructor attempts to check if SteamInput() != nullptr, but the required Steam DLL has already been unloaded. This occurs on both Steam and EGS clients on Windows.

Game thread callstack:

RaiseException __delayLoadHelper2 (delayhlp.cpp:301) _tailMerge_steam_api64_dll [inlined] SteamInput (isteaminput.h:970) FSteamController::~FSteamController (SteamController.cpp:116) destructor' [inlined] SharedPointerInternals::TReferenceControllerBase<T>::ReleaseSharedReference (SharedPointerInternals.h:227) [inlined] SharedPointerInternals::FSharedReferencer<T>::{dtor} (SharedPointerInternals.h:606) DestructItems<T> (MemoryOps.h:108) [inlined] TArray<T>::{dtor} (Array.h:684) FWindowsApplication::~FWindowsApplication (WindowsApplication.cpp:382) destructor' [inlined] SharedPointerInternals::TReferenceControllerBase<T>::ReleaseSharedReference (SharedPointerInternals.h:227) SharedPointerInternals::FSharedReferencer<T>::operator= (SharedPointerInternals.h:653) [inlined] TSharedPtr<T>::operator= (SharedPointer.h:931) [inlined] TSharedPtr<T>::Reset (SharedPointer.h:1092) FSlateApplication::Shutdown (SlateApplication.cpp:796) FEngineLoop::Exit (LaunchEngineLoop.cpp:5106) [inlined] EngineExit (Launch.cpp:80)

Do you have any recommendations as to how we can avoid this race condition?

Hi Andrew,

I do not have a SteamController to fully test but the DLL should still be loaded at that time. I did a debugging session and the DLL is loaded in FSteamSharedModule::LoadSteamModules and unloaded in FSteamSharedModule::UnloadSteamModules both are inlined so you will need to monitor FSteamSharedModule::StartupModule\FSteamSharedModule::ShutdownModule which invoke the other 2.

The Shutdown\Unload one is the most interesting here as they happen after FSlateApplication::Shutdown so the DLL should still be loaded. Can you share the log? I’m interested to know which exception is being thrown and check for other related messages.

I do see that there is a method that can validate the DLL is available in FSteamSharedModule so a quick workaround could probably be:

virtual ~FSteamController() { if (FSteamSharedModule::Get().AreSteamDllsLoaded() && SteamInput() != nullptr) { SteamInput()->Shutdown(); } }

There is nothing useful in the log unfortunately. The exception might be logged in CrashContext.runtime-xml if you are collecting that file.

I should mention that I didn’t try to compile the code snippet so you might have to fiddle a bit. Let me know if that helps, I could add it for the future.

Now that we’ve got a release out, I’m not seeing the crash anymore so I do believe that fix resolved the issue.

Thanks for the update. I communicated the problem and the fix to the owner of the SteamController code. The fix should make it in a future release.

Thanks, that sounds like a good check to perform so I’ll try adding that to see if it makes a difference for now.

Regarding the logs, we’ve only had external users hitting this crash so I had to redact some information from one of them (starting from the RequestExit), but maybe that will still be useful.

I’ll let you know if we see any change with that modification once we deploy a new release.

Ah yeah, we do have that (attached).

And the snippet seems to be fine from some brief internal testing. We’ll keep an eye on it once it’s deployed to see if it makes a noticeable difference.