View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0001898 | NoesisGUI | C++ SDK | public | 2021-01-21 16:38 | 2021-01-21 18:16 |
Reporter | steveh | Assigned To | hcpizzi | ||
Priority | normal | Severity | crash | Reproducibility | random |
Status | resolved | Resolution | fixed | ||
Product Version | 3.0.6 | ||||
Target Version | 3.0.10 | Fixed in Version | 3.0.10 | ||
Summary | 0001898: Non thread safe code in InspectorTextureProvider | ||||
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 & <args_1>) 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 | ||||
Tags | No tags attached. | ||||
Platform | Any | ||||
Date Modified | Username | Field | Change |
---|---|---|---|
2021-01-21 16:38 | steveh | New Issue | |
2021-01-21 17:04 | jsantos | Assigned To | => hcpizzi |
2021-01-21 17:04 | jsantos | Status | new => assigned |
2021-01-21 17:04 | jsantos | Target Version | => 3.0.10 |
2021-01-21 18:16 | hcpizzi | Status | assigned => resolved |
2021-01-21 18:16 | hcpizzi | Resolution | open => fixed |
2021-01-21 18:16 | hcpizzi | Fixed in Version | => 3.0.10 |
2021-01-21 18:16 | hcpizzi | Note Added: 0006978 |