This happens because when initializing the screen reader dependency it calls `ISlateScreenReaderModule::Get()` which will add an entry to `GlobalDynamicSystemModuleMap` when it loads the SlateScreenReader module. Unfortunately, the code in `FSubsystemCollectionBase::Initialize` is iterating that map during subsystem initialization so we hit the following ensure in `TSparseArray<>::TRangedForIterator<>::operator!=`.
Any advice what I should be doing here instead to avoid this ensure? Should this loop in `FSubsystemCollectionBase::Initialize` be changed to use a different iterator that is tolerant to the container changing?
FORCEINLINE bool operator!=(const TRangedForIterator& Rhs) const { // We only need to do the check in this operator, because no other operator will be // called until after this one returns. // // Also, we should only need to check one side of this comparison - if the other iterator isn't // even from the same array then the compiler has generated bad code. ensureMsgf(this->Array.Num() == InitialNum, TEXT("Container has changed during ranged-for iteration!")); return *(TIterator*)this != *(TIterator*)&Rhs; }
Steps to Reproduce
Create a `UEngineSubsystem` in a game module that accesses `USlateScreenReaderEngineSubsystem` as a dependency in `Initialize()`.
For example:
`UCLASS()
class NEBULA_API UMySubsystem : public UEngineSubsystem
{
GENERATED_BODY()
virtual void Initialize(FSubsystemCollectionBase& Collection) override
{
Super::Initialize(Collection);
// This will cause an ensure to fire
USlateScreenReaderEngineSubsystem* ScreenReaderSubsystem = Collection.InitializeDependency();
}
}`
Hi Chad,
I think there’s a larger issue of load order fragility here that would be nice to address, but the main problem in this case is that the SlateScreenReader plugin has it’s load phase set to PostEngineInit. Modules can forcibly load other modules earlier than their specified phase, and in this case the engine subsystem is causing an early load. The easiest way to avoid this would be to just bump the SlateScreenReader plugin to load earlier and be sure it already exists prior to the subsystem accessing it. I tried setting the load phase to PostDefault and it seemed to resolve the issue without causing any problems with the screen reader, so that might be the quickest path forward here.
Best,
Cody
Hi,
So you changed both the SlateScreenReader subsystem and your subsystem to both be GameInstance? That should be fine as long as you didn’t want to use the screen reader in the editor at all, since it wouldn’t be initialized until runtime. Outside that, I don’t see any concerns.
Best,
Cody
I ended up changing this to be a GameInstance subsystem instead. On the game side I wanted to access game settings in the TTS factory, and Engine subsystems were too early to handle that.
By making this a game instance subsystem I was able to ensure the modules are loaded first, that I can use the Collection.InitializeDependency to ensure subsystem ordering, and am able to access the data I require in my factory for TTS. Having my subsystem be GameInstance and the SlateScreenReader one be EngineSubsystem would sometimes initialize out of order in some builds, and I didn’t have a good tool to fix that ordering (aside from maybe LoadPhase).