-
- christyjquinn
- Posts: 17
- Joined:
Commands and being stack allocated in view models
Hello,
In the commands tutorial there is a note about stack allocating commands:
Thanks!
In the commands tutorial there is a note about stack allocating commands:
Why is it safe in this case and in what cases is it not safe?In this sample DelegateCommand is not allocated in the heap to reduce allocations as explained in the C++ Architecture Guide. This is one of those scenarios where it is safe to do so and reference counting is not needed.
Thanks!
-
- christyjquinn
- Posts: 17
- Joined:
Re: Commands and being stack allocated in view models
A little more context for why I am asking this. I'm sometimes hitting a crash where a stack allocated command is decremented after being released. The command is stack allocated in a view model and bound to a button and game pad trigger as follows:
And here is the stack trace:
Code: Select all
<b:Interaction.Triggers>
<noesis:GamepadTrigger FiredOn="ButtonDown" Button="Cancel">
<b:InvokeCommandAction Command="{Binding CloseCommand}" />
</noesis:GamepadTrigger>
</b:Interaction.Triggers>
...
<Button Command="{Binding CloseCommand}">
...
Code: Select all
#0 0x10034d763 in Noesis::AtomicInteger::FetchAndDecrement() Atomic.inl:57
#1 0x10034d6f6 in Noesis::AtomicInteger::operator--() Atomic.inl:123
#2 0x10034d620 in Noesis::BaseRefCounted::InternalRelease() const BaseRefCounted.inl:63
#3 0x10034ef76 in Noesis::BaseRefCounted::Release() const BaseRefCounted.inl:37
#4 0x105dd70ef in Noesis::Ptr<Noesis::BaseComponent>::~Ptr() Ptr.inl:77
#5 0x105dd7044 in Noesis::Ptr<Noesis::BaseComponent>::~Ptr() Ptr.inl:74
#6 0x100990fcc in Noesis::ValueStorageManagerImpl<Noesis::Ptr<Noesis::BaseComponent> >::Destroy(void**) const ValueStorageManagerImpl.inl:121
#7 0x10673d11f in Noesis::StoredValue::DestroyAllValues(Noesis::DependencyProperty const*)
#8 0x106737384 in Noesis::DependencyObject::~DependencyObject()
#9 0x103cf3509 in NoesisApp::AttachableObject::~AttachableObject() AttachableObject.cpp:32
#10 0x103e1ae06 in NoesisApp::TriggerAction::~TriggerAction() TriggerAction.Interactivity.cpp:24
#11 0x103ddd386 in NoesisApp::TargetedTriggerAction::~TargetedTriggerAction() TargetedTriggerAction.cpp:28
#12 0x103d84494 in NoesisApp::TargetedTriggerActionT<Noesis::DependencyObject>::~TargetedTriggerActionT() TargetedTriggerAction.h:88
#13 0x103d84657 in NoesisApp::InvokeCommandAction::~InvokeCommandAction() InvokeCommandAction.cpp:25
#14 0x103d84674 in NoesisApp::InvokeCommandAction::~InvokeCommandAction() InvokeCommandAction.cpp:24
#15 0x103d84718 in NoesisApp::InvokeCommandAction::~InvokeCommandAction() InvokeCommandAction.cpp:24
#16 0x106739212 in Noesis::DependencyObject::OnDestroy() const
#17 0x10675116c in Noesis::BaseFreezableCollection::~BaseFreezableCollection()
#18 0x103e1ef64 in Noesis::FreezableCollection<NoesisApp::TriggerAction>::~FreezableCollection() FreezableCollection.h:26
#19 0x103e1c82e in NoesisApp::AttachableCollection<NoesisApp::TriggerAction>::~AttachableCollection() AttachableCollection.inl:26
#20 0x103e1bf9b in NoesisApp::TriggerActionCollection::~TriggerActionCollection() TriggerActionCollection.cpp:29
#21 0x103e1c8f4 in NoesisApp::TriggerActionCollection::~TriggerActionCollection() TriggerActionCollection.cpp:20
#22 0x103e1c9f8 in NoesisApp::TriggerActionCollection::~TriggerActionCollection() TriggerActionCollection.cpp:20
#23 0x106739212 in Noesis::DependencyObject::OnDestroy() const
#24 0x10034d6aa in Noesis::BaseRefCounted::InternalRelease() const BaseRefCounted.inl:67
#25 0x10034ef76 in Noesis::BaseRefCounted::Release() const BaseRefCounted.inl:37
#26 0x105dd70ef in Noesis::Ptr<Noesis::BaseComponent>::~Ptr() Ptr.inl:77
#27 0x105dd7044 in Noesis::Ptr<Noesis::BaseComponent>::~Ptr() Ptr.inl:74
#28 0x100990fcc in Noesis::ValueStorageManagerImpl<Noesis::Ptr<Noesis::BaseComponent> >::Destroy(void**) const ValueStorageManagerImpl.inl:121
#29 0x106737384 in Noesis::DependencyObject::~DependencyObject()
#30 0x103cf3509 in NoesisApp::AttachableObject::~AttachableObject() AttachableObject.cpp:32
#31 0x103e22156 in NoesisApp::TriggerBase::~TriggerBase() TriggerBase.Interactivity.cpp:34
#32 0x103d6a454 in NoesisApp::TriggerBaseT<Noesis::UIElement>::~TriggerBaseT() TriggerBase.h:92
#33 0x103d6a436 in NoesisApp::GamepadTrigger::~GamepadTrigger() GamepadTrigger.cpp:28
#34 0x103d6a474 in NoesisApp::GamepadTrigger::~GamepadTrigger() GamepadTrigger.cpp:27
#35 0x103d6a518 in NoesisApp::GamepadTrigger::~GamepadTrigger() GamepadTrigger.cpp:27
#36 0x106739212 in Noesis::DependencyObject::OnDestroy() const
#37 0x10675116c in Noesis::BaseFreezableCollection::~BaseFreezableCollection()
#38 0x103dd6224 in Noesis::FreezableCollection<NoesisApp::TriggerBase>::~FreezableCollection() FreezableCollection.h:26
#39 0x103e2728e in NoesisApp::AttachableCollection<NoesisApp::TriggerBase>::~AttachableCollection() AttachableCollection.inl:26
#40 0x103e26c0b in NoesisApp::TriggerCollection::~TriggerCollection() TriggerCollection.cpp:29
#41 0x103e27354 in NoesisApp::TriggerCollection::~TriggerCollection() TriggerCollection.cpp:20
#42 0x103e27458 in NoesisApp::TriggerCollection::~TriggerCollection() TriggerCollection.cpp:20
#43 0x106739212 in Noesis::DependencyObject::OnDestroy() const
#44 0x10034d6aa in Noesis::BaseRefCounted::InternalRelease() const BaseRefCounted.inl:67
#45 0x10034ef76 in Noesis::BaseRefCounted::Release() const BaseRefCounted.inl:37
#46 0x105dd70ef in Noesis::Ptr<Noesis::BaseComponent>::~Ptr() Ptr.inl:77
#47 0x105dd7044 in Noesis::Ptr<Noesis::BaseComponent>::~Ptr() Ptr.inl:74
#48 0x100990fcc in Noesis::ValueStorageManagerImpl<Noesis::Ptr<Noesis::BaseComponent> >::Destroy(void**) const ValueStorageManagerImpl.inl:121
#49 0x106737384 in Noesis::DependencyObject::~DependencyObject()
#50 0x1067c486c in Noesis::FrameworkElement::~FrameworkElement()
#51 0x100607b04 in Noesis::UserControl::~UserControl() UserControl.h:23
#52 0x105d4cce4 in MyView::~MyView() MyView.xaml.h:7
#53 0x105d4cbb4 in MyView::~MyView() MyView.xaml.h:7
#54 0x105d4cbd8 in MyView::~MyView() MyView.xaml.h:7
#55 0x106739212 in Noesis::DependencyObject::OnDestroy() const
#56 0x10034d6aa in Noesis::BaseRefCounted::InternalRelease() const BaseRefCounted.inl:67
#57 0x1005cc016 in Noesis::FrameworkElement::Release() const FrameworkElement.h:511
#58 0x1068fa475 in Noesis::UIElement::BubblingEvent(Noesis::RoutedEventArgs const&)
#59 0x10683d5bf in Noesis::Keyboard::KeyDown(Noesis::Key)
-
-
sfernandez
Site Admin
- Posts: 2866
- Joined:
Re: Commands and being stack allocated in view models
Hi,
In your example, the ViewModel set in the DataContext property is probably destroyed before the Triggers property is being released in the UserControl root, and that is why you are getting a crash. In situations where you can't make sure that the ViewModel will live longer than the references to the command, you have to use heap allocated commands.
In our Commands sample it is safe to use a stack allocated command because the lifetime of the ViewModel defining that command is longer than the Button using it.Why is it safe in this case and in what cases is it not safe?
In your example, the ViewModel set in the DataContext property is probably destroyed before the Triggers property is being released in the UserControl root, and that is why you are getting a crash. In situations where you can't make sure that the ViewModel will live longer than the references to the command, you have to use heap allocated commands.
Re: Commands and being stack allocated in view models
In profile configurations (NS_PROFILE enabled), BaseRefCounted has a check to detect problems with this:
If you created the object in the stack, at destruction time the reference counter must be 1. If not, an error is raised.
Code: Select all
inline BaseRefCounted::~BaseRefCounted()
{
NS_CHECK([&]()
{
// Note that 1 is valid when the object lives is the stack or is being manually destroyed
int32_t n = mRefCount.Load();
NS_CHECK(n == 1 || n == 0, "Unexpected RefCount(%d) deleting object at %p", n, this);
return true;
}
(), "_");
}
-
- christyjquinn
- Posts: 17
- Joined:
Re: Commands and being stack allocated in view models
Awesome thanks both, I suppose if we aren't having performance issues there is no harm allocating on the heap by default.
Who is online
Users browsing this forum: No registered users and 1 guest