We experienced crashes when running Debug build of Noesis.
The callstack is as follows:
CallStack[27280]: 7FF9A9538EB6h : Noesis.dll (7FF9A8E80000h) : Noesis::BaseVector<Noesis::BaseComponent >::Erase + 96h : Vector.inl (561) + 30h
CallStack[27280]: 7FF9A952E269h : Noesis.dll (7FF9A8E80000h) : OnDependencyObjectDestroyed + E9h : UI.cpp (763) + 0h
CallStack[27280]: 7FF9A953AC6Ch : Noesis.dll (7FF9A8E80000h) : Noesis::Delegate<void __cdecl(Noesis::DependencyObject )>::FreeFuncStub<void (cdecl)(Noesis::DependencyObject )>::Invoke + 3Ch : Delegate.inl (397) + 0h
CallStack[27280]: 7FF9A8F5D973h : Noesis.dll (7FF9A8E80000h) : Noesis::Delegate<void cdecl(Noesis::DependencyObject )>::operator() + 53h : Delegate.inl (173) + 0h
CallStack[27280]: 7FF9A8F85DAAh : Noesis.dll (7FF9A8E80000h) : Noesis::Delegate<void __cdecl(Noesis::DependencyObject )>::MultiDelegate::Invoke + CAh : Delegate.inl (573) + 0h
CallStack[27280]: 7FF9A8F5D973h : Noesis.dll (7FF9A8E80000h) : Noesis::Delegate<void __cdecl(Noesis::DependencyObject *)>::operator() + 53h : Delegate.inl (173) + 0h
CallStack[27280]: 7FF9A8F519DCh : Noesis.dll (7FF9A8E80000h) : Noesis::DependencyObject::OnDestroy + 4Ch : DependencyObject.cpp (557) + 0h
CallStack[27280]: 7FF9A91BC7DEh : Noesis.dll (7FF9A8E80000h) : Noesis::FrameworkElement::OnDestroy + EEh : FrameworkElement.cpp (1632) + 0h
CallStack[27280]: 7FF9A8F22941h : Noesis.dll (7FF9A8E80000h) : Noesis::BaseRefCounted::Release + 91h : BaseRefCounted.inl (41) + 10h
CallStack[27280]: 7FF9A8F7804Bh : Noesis.dll (7FF9A8E80000h) : Noesis::FrameworkElement::Release + 3Bh : FrameworkElement.h (542) + 11h
Debugging the issue, we found the root problem is inside AddLoadedXaml. The problematic sequence is as follows:
-
"objects" reference is initialized as gLoadedXamls[uri.Str()];
-
"OnDependencyObjectDestroyed(object);" is called
-> internally it may erase entry inside gLoadedXamls, and the internal implementation of gLoadedXamls (HashMap) may reorganize its physical storage
-
"objects.PushBack(component);" is called
-> if gLoadedXamls is reorganized in the previous step, the "objects" reference we retrieved before is no longer valid, thus storing the component into it is not correctly updated gLoadedXamls.
static void AddLoadedXaml(BaseComponent component, const Uri& uri)
{
DependencyObject object = DynamicCast<DependencyObject>(component);
ResourceDictionary dictionary = DynamicCast<ResourceDictionary*>(component);
if (object || dictionary)
{
Vector<BaseComponent*>& objects = gLoadedXamls[uri.Str()];
if (objects.Find(component) == objects.End())
{
if (object != nullptr)
{
OnDependencyObjectDestroyed(object);
object->Destroyed() += &OnDependencyObjectDestroyed;
objects.PushBack(component);
gComponentXamls[component] = uri.Str();
}
else if (dictionary != nullptr)
{
OnResourceDictionaryDestroyed(dictionary);
dictionary->Destroyed() += &OnResourceDictionaryDestroyed;
objects.PushBack(component);
gComponentXamls[component] = uri.Str();
}
}
}
} |