| Description | Hi guys, we're hitting an assert at random intervals when loading textures and creating bitmap image render proxies at runtime. The assert is coming from hashmap when we're growing the bucket container:
template<typename Bucket>
inline void HashMapImpl<Bucket>::InsertBucketsFrom(Bucket src, uint32_t count)
{
for (Bucket it = src; it != src + count; ++it)
{
if (!it->IsEmpty())
{
// Insert the key/value into the new table.
Bucket* bucket;
bool found = FindInsertBucket(it->Hash(), it->key, bucket);
NS_ASSERT(!found); // <--- HERE
new (bucket) Bucket(MoveArg(*it));
it->~Bucket();
mNumEntries++;
}
}
}
So I double checked, all the buckets in the source were unique so something must have added the key during the while loop. It turns out that there are no locks in InspectorTextureProvider, so for example this runs on render thread:
Noesis::InspectorTextureProvider::OpenStream(const char uri) Line 76 C++
Noesis::InspectorTextureProvider::LoadTexture(const char uri, Noesis::RenderDevice device) Line 196 C++
Noesis::VGLContext::CreateImage(const char filename) Line 544 C++
Noesis::BitmapImageProxy::CreateImage(Noesis::RenderTree tree) Line 45 C++
Noesis::ImageSourceProxy::GetImage(Noesis::RenderTree tree) Line 34 C++
Noesis::ImageBrushProxy::UpdateImageSource(Noesis::RenderTree tree, Noesis::ImageSourceProxy is) Line 34 C++
Noesis::RenderTree::ProcessCommand(Noesis::RenderCommandId id, const void data) Line 516 C++
Noesis::RenderTree::ProcessRenderCommands(const unsigned char data, unsigned int size) Line 254 C++
Noesis::Renderer::UpdateRenderTree() Line 122 C++
And then we call Image::SetSource() on main thread:
InspectorTextureProvider::OpenStream(const char uri) Line 76 C++
InspectorTextureProvider::GetTextureInfo(const char uri) Line 164 C++
BitmapImage::UpdateImageInfo(const char uri) Line 204 C++
BitmapImage::StaticFillClassType::__l2::<lambda>(Noesis::DependencyObject d, const Noesis::DependencyPropertyChangedEventArgs & e) Line 249 C++
Delegate<void cdecl(Noesis::DependencyObject ,Noesis::DependencyPropertyChangedEventArgs const &)>::FunctorStub<void <lambda>(Noesis::DependencyObject , const Noesis::DependencyPropertyChangedEventArgs &) >::Invoke(Noesis::DependencyObject * <args_0>, const Noesis::DependencyPropertyChangedEventArgs & <args_1>) Line 432 C++
Delegate<void cdecl(Noesis::DependencyObject ,Noesis::DependencyPropertyChangedEventArgs const &)>::operator()(Noesis::DependencyObject <args_0>, const Noesis::DependencyPropertyChangedEventArgs & <args_1>) Line 173 C++
DependencyObject::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 607 C++
Freezable::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & e) Line 235 C++
DependencyObject::Init() Line 536 C++
InitComponent(Noesis::IComponentInitializer component, bool doInit) Line 41 C++
TemplateBindingExpression::Evaluate() Line 76 C++
DependencyObject::InternalSetExpression(const Noesis::DependencyProperty dp, Noesis::Expression newExpression, unsigned char priority) Line 676 C++
DependencyObject::InternalInvalidateProperty(const Noesis::DependencyProperty dp, unsigned char priority) Line 1109 C++
DependencyObject::InvalidateProperty(const Noesis::DependencyProperty dp, unsigned char priority) Line 291 C++
TemplateBindingExpression::OnTemplatedParentPropertyChanged(Noesis::BaseComponent formal, const Noesis::DependencyPropertyChangedEventArgs & args) Line 186 C++
Delegate<void cdecl(Noesis::BaseComponent ,Noesis::DependencyPropertyChangedEventArgs const &)>::MemberFuncStub<Noesis::TemplateBindingExpression,void (__cdecl Noesis::TemplateBindingExpression::)(Noesis::BaseComponent ,Noesis::DependencyPropertyChangedEventArgs const &)>::Invoke(Noesis::BaseComponent <args_0>, const Noesis::DependencyPropertyChangedEventArgs & <args_1>) Line 469 C++
[Inline Frame] Noesis::Delegate<void cdecl(Noesis::BaseComponent ,Noesis::DependencyPropertyChangedEventArgs const &)>::operator()(Noesis::BaseComponent ) Line 172 C++
Noesis::Delegate<void cdecl(Noesis::BaseComponent ,Noesis::DependencyPropertyChangedEventArgs const &)>::MultiDelegate::Invoke(Noesis::BaseComponent <args_0>, const Noesis::DependencyPropertyChangedEventArgs & <args_1>) Line 570 C++
Noesis::Delegate<void __cdecl(Noesis::BaseComponent ,Noesis::DependencyPropertyChangedEventArgs const &)>::operator()(Noesis::BaseComponent <args_0>, const Noesis::DependencyPropertyChangedEventArgs & <args1>) Line 173 C++
Noesis::DependencyObject::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 608 C++
Noesis::Visual::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 905 C++
Noesis::UIElement::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 2013 C++
Noesis::FrameworkElement::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 1660 C++
Noesis::Control::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 443 C++
Noesis::Image::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 137 C++
Noesis::DependencyObject::NotifyPropertyChanged(const Noesis::DependencyProperty dp, Noesis::StoredValue storedValue, const void oldValue, const void newValue, bool valueChanged, bool isBaseComponent, const Noesis::PropertyMetadata metadata) Line 1260 C++
Noesis::DependencyObject::InternalSetValue(const Noesis::DependencyProperty dp, void oldValue, const void newValue, void coercedValue, unsigned char priority, Noesis::Expression newExpression, const Noesis::PropertyMetadata * metadata, Noesis::Value::Destination destination, bool isBaseComponent) Line 894 C++
Noesis::DependencyObject::SetValue<Noesis::Ptr<Noesis::ImageSource> >(Noesis::Int2Type<1> __formal, const Noesis::DependencyProperty dp, Noesis::ImageSource value, Noesis::Value::Destination destination) Line 195 C++
Noesis::DependencyObject::SetValue<Noesis::Ptr<Noesis::ImageSource> >(const Noesis::DependencyProperty dp, Noesis::ImageSource value) Line 60 C++
Noesis::Image::SetSource(Noesis::ImageSource *) Line 40 C++
Is this unintended behaviour? I can add locks in InspectorTextureProvider but I don't know if we're supposed to be calling SetSource from main thread, I assumed we were. E.g. we do the following in our game:
String szFullPath = "Assets/UI/Image1.png";
Ptr<BitmapImage> pImageSource = MakePtr<BitmapImage>(Uri(szFullPath.Str()));
pImageControl->SetSource(pImageSource);
This works almost all the time, the only time it doesn't is when the internal map in the InspectorTextureProvider asserts due to being mutated on multiple threads.
Any ideas? Cheers,
-Steven |
|---|